Problem/Motivation

  1. Layout Builder doesn't support translatable layouts but we still have the "Layout" tab when viewing an entity translation. In 8.6.x translations do not work at all. After #3004536: Move the Layout Builder UI into an entity form for better integration with other content authoring modules and core features there was sort broken support for translations but this was not backported to 8.7.x.
  2. We also need to make sure that @FieldType=layout_section is not translatable.

Here is the problem that you could get translated, but not updatable, layout overrides in 8.6.x .

  1. The layout tab for overrides always showed and edited the default language layout even on a translations layout tab
  2. The only way to get a layout override into the non-default language field value was to create an override before you create a translation. Then the layout would be copied over on creating the translation
  3. In that case the view of the translation would show the copied over sections but the layout builder would continue to show/edit the default language layout.
  4. The non-default translation entity view would be stuck with a layout you could never change.
  5. if the user never went to the layout tab for the translation and after this only went to the layout tab for the default translation then the user may not have every known they could not translate the layout further.
  6. The user could continue to alter the layout for the default language
  7. Given the last 2 points the user may have actually not known they had an unchangable translated layout override and from their perspective they were able to translate the layout because they had different layouts for each language

In addition to this translations created when the untranslated entity had no layout override would always show the default layout configuration for the bundle. The user may have perceived this behavior to be the ability have a layout override only for the default language.

If we updated all layout fields to non-translatable then:

  1. When the user viewed the entity translations Drupal would only load the default translation layout so they would see a different layout than before the update.
  2. Furthermore if they looked at their revision history of a translation they would also see the default translation layout. So their revision history would look different
  3. The translated layout sections would still exist in the database until the user edited the translation(not the layout which is not possible). At that point the translation sections would be deleted/cleaned up because it was no longer a translated field
  4. Also entity translations that had no translated layouts but the untranslated entity had a layout override would switch from using the bundle default layout to the untranslated layout override.

Because of the above we cannot set any layout fields to untranslatable because different entities could be in different states and different users depending on if they ever tried to edit a translated layout may not perceive the current behavior to be a bug. But we also cannot allow them to update the translated layouts because we don't actually support that.

Users may be relying on the ability to configure the default translation layout override and have the translations be unaffected(even though this was not a intentional feature)

Proposed resolution

Now that #2946333: Allow synced Layout override Translations: translating labels and inline blocks has been postponed we now need to take a few steps to make sure translation support is not exposed to users.

  1. Ensure that the "Layouts" tab is not accessible on entity translations.
  2. Any new layout fields added should be untranslatable.

In the Change Record we need to explain that to obtain the current module functionality for overrides for existing layout fields the must be manually set to non-translatable. It also details the implications of doing that.

We will not put a message in the UI either on the Content Translation configuration page or on the status report page because there is no way to know whether the field is translatable because the module was installed before this issue or if the field is translatable intentionally because they have a contrib module or custom code that uses the translatable fields to actually implement translations.

Remaining tasks

fixes, tests, review

User interface changes

No "Layout" tab when viewing entity translations.

API changes

None.

Data model changes

All @FieldType=layout_section instances are marked as untranslatable.

Release notes snippet

Layout Builder overrides are now stored in non-translatable fields. This means that on entities with overridable layouts, the Layout tab is only available when viewing the entity in its default language (not from a translation) and layout changes made from there apply to all translations. For sites that installed Layout Builder and enabled layout overrides for some entity bundles while it was still Experimental (prior to 8.7.0-beta1), this release retains the site's existing translatability configuration of those fields. For those sites, manually setting the layout field to non-translatable is strongly recommended unless there is existing layout translation data. See the change record for more details.

CommentFileSizeAuthor
#81 3041659-translation-81.patch21.9 KBtim.plunkett
#62 3041659-translation-62-interdiff.txt1.82 KBtim.plunkett
#62 3041659-translation-62.patch22.08 KBtim.plunkett
#61 3041659-translation-61-interdiff.txt2.48 KBtim.plunkett
#61 3041659-translation-61.patch21.42 KBtim.plunkett
#58 3041659-translation-58-interdiff.txt3.55 KBtim.plunkett
#58 3041659-translation-58-PASS.patch21.42 KBtim.plunkett
#58 3041659-translation-58-FAIL.patch21.42 KBtim.plunkett
#57 3041659-TranslatableFieldTest_why_oh_why_do_you_pass.patch24.64 KBtedbow
#57 3041659-57.patch22.55 KBtedbow
#57 interdiff-reroll-57.txt4.03 KBtedbow
#56 3041659-56.patch22.52 KBtedbow
#56 3041659-TranslatableFieldTest_why_oh_why_do_you_pass.patch24.61 KBtedbow
#56 interdiff-reroll-56.txt4 KBtedbow
#52 3041659-52-simple-no-tab.patch19.99 KBtedbow
#50 3041659-46.patch63.9 KBtedbow
#50 interdiff-43-46.txt9.64 KBtedbow
#45 Content_language___Drupal_8_x.png30.31 KBplach
#43 3041659-43.patch62.87 KBtedbow
#43 interdiff-42-43.txt1.17 KBtedbow
#42 3041659-44-reroll.patch62.46 KBtedbow
#41 3041659-40.patch62.58 KBtedbow
#41 interdiff-37-41.txt2.93 KBtedbow
#37 3041659-37.patch62.48 KBtedbow
#37 interdiff-35-37.txt6.59 KBtedbow
#37 status_page.png23.26 KBtedbow
#37 warning_content_translation.png20.56 KBtedbow
#35 3041659-35.patch58.39 KBtedbow
#35 interdiff-33-35.patch9.84 KBtedbow
#33 interdiff-30-33.txt5.48 KBtedbow
#33 3041659-33.patch61.48 KBtedbow
#33 3041659-33-do-not-test.patch58.5 KBtedbow
#30 interdiff-26-30.txt19.09 KBtedbow
#30 3041659-translation-30-do-not-test.patch58.14 KBtedbow
#30 3041659-translation-30-plus-3042993.patch61.12 KBtedbow
#29 3041659-29-combined.patch52.17 KBamateescu
#26 interdiff-26.txt6.19 KBamateescu
#26 3041659-26.patch49.18 KBamateescu
#24 interdiff-20-24.txt4.57 KBtedbow
#24 3041659-24.patch50.62 KBtedbow
#20 3041659-20.patch50.33 KBtedbow
#20 interdiff-15-20.txt6.68 KBtedbow
#15 3041659-15.patch47.64 KBtedbow
#15 interdiff-10-PASS-15.txt9 KBtedbow
#3 3041659-2.patch9.51 KBtedbow
#6 interdiff-3-5.txt6.28 KBtedbow
#6 3041659-5.patch15.79 KBtedbow
#10 3041659-translation-10-PASS.patch40.25 KBtim.plunkett
#10 3041659-translation-10-FAIL.patch24.46 KBtim.plunkett

Comments

tedbow created an issue. See original summary.

tim.plunkett’s picture

tedbow’s picture

Status: Active » Needs review
StatusFileSize
new9.51 KB

Here is patch with tests.

  1. Add a test for when you create the translation first and then create the layout override and also in the other order.

    I did this because 8.6.x has a problem if you add layout override first because when you create the translation the layout value are copied over. but then you can never change the translated layout because the UI only affects the untranslated layout field value.

  2. What should we do about sites that already have layout builder and content moderation enable together with layout builder has a translatable field?

    Should we attempt to make the field untranslatable? Will this remove all translated field values?

    Otherwise if we don't want change existing fields we still need make sure that fact they have the layout field as translatable doesn't cause problems with new entities and translations.

    To do this we would have to implement hook_entity_translation_create and make sure when a translation is created the layout field values are copied over. Otherwise the translation would always use the layout override at the time the translation was created.

Status: Needs review » Needs work

The last submitted patch, 3: 3041659-2.patch, failed testing. View results

tedbow’s picture

Status: Needs work » Needs review

Remove test that depended on access the layout test on translations.

tedbow’s picture

StatusFileSize
new6.28 KB
new15.79 KB

whoops here is the patch

plach’s picture

  1. +++ b/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
    @@ -362,6 +362,8 @@ public function save() {
    +    $result = $result->andIf(AccessResult::allowedIf(!($entity instanceof TranslatableInterface && !$entity->isDefaultTranslation())))->addCacheableDependency($entity);
    

    Can we add an inline comment to explain that we do not support translations yet and maybe a @todo pointing to #2946333: Allow synced Layout override Translations: translating labels and inline blocks?

  2. +++ b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTranslationTest.php
    @@ -0,0 +1,217 @@
    +    $entity_url = $this->entity->toUrl('canonical')->toString();
    ...
    +    $entity_url = $this->entity->toUrl('canonical')->toString();
    

    No need for canonical here.

  3. +++ b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTranslationTest.php
    @@ -0,0 +1,217 @@
    +    $this->drupalLogin($this->translator);
    

    I guess we should restore the previous user after the translation has been created.

plach’s picture

[duplicate comment]

tedbow’s picture

@plach thanks for both these reviews 😜

I talked with @plach about what to do about existing sites with override.

First the behavior in 8.6.x for translations:

  1. The layout tab for overrides alway showed and edited the default language layout even on translation layout tab
  2. The only way to get a layout override into the non-default language field value was to create an override before you create a translation. Then the layout would be copied over on creating the translation
  3. In that case the view of the translation would show the copied over sections but the layout builder would continue to show/edit the default language layout.
  4. The non-default entity view would be stuck with a layout you could never change.

The idea we had for the new update hook would be to set all existing layout fields as non-translatable.

  1. This would immediatelyy cause all entities of translation overrides to use the default translation overrides.
  2. The only change for existing sites would be if you were stuck with layout you couldn't change as described in 8.6.x behavior above. You would now see the non-translated layout on all translation(because it is non transtable).
  3. The next time an entity with an override was saved it would remove the data from the new saved revision. If an entity didn't save with a new revision it would just remove the saved translated layout.

So the update itself would not remove any data from the database but the next entity save could if a new revision was not made.
We could add in the Change Record for this issue to mention this and that if somehow a site had got translation overrides to work in 8.6.x they would immediately have to set the field to translatable again after the update. Of course they would also have to probably update whatever custom code they had that get to override translations to work correctly in 8.6.x because this same code would almost for sure not work in 8.7.x.

Also of course there would be no guarantee that whatever custom way that a site got translated layout overrides to work would be compatible with the way that core will eventually implement for layout override translations in 8.8.0(or whenever it happens).

tim.plunkett’s picture

Here's an update path test.
It passes by itself, and fails without the update path.
Once the update path is written, it should fail with that but without the fix, and pass all combined.
FAIL patch is the interdiff.

The last submitted patch, 10: 3041659-translation-10-FAIL.patch, failed testing. View results

The last submitted patch, 10: 3041659-translation-10-PASS.patch, failed testing. View results

tim.plunkett’s picture

Assigned: Unassigned » tim.plunkett

At least they were different fails...
Looking into it

plach’s picture

  1. +++ b/core/modules/layout_builder/tests/fixtures/update/layout-builder-translation.php
    @@ -0,0 +1,126 @@
    +$node_field_data['revision_translation_affected'] = NULL;
    ...
    +$node_field_revision['revision_translation_affected'] = NULL;
    

    Why NULL? These will be 1 if you create a new translation while updating the default revision. It likely doesn't matter but better safe than sorry :)

  2. +++ b/core/modules/layout_builder/tests/src/Functional/Update/MakeLayoutUntranslatableUpdatePathTest.php
    @@ -0,0 +1,110 @@
    +    $this->drupalGet("admin/structure/types/manage/$type/display");
    

    Why do we need this?

  3. +++ b/core/modules/layout_builder/tests/src/Functional/Update/MakeLayoutUntranslatableUpdatePathTest.php
    @@ -0,0 +1,110 @@
    +    $this->drupalGet('es' . $node->toUrl()->toString());
    

    Ideally we would do
    $node->getTranslation('es')->toUrl(), but we'd need to reload the node first...

tedbow’s picture

StatusFileSize
new9 KB
new47.64 KB

@plach thanks for the review. I didn't get chance to address in this patch
This patch does

  1. Creates an post_update hook to set fields to non-translatable if the bundle has no translated layouts
  2. If the field is still translatable clears out sections on new translations via hook_entity_translation_create
  3. Gives access to the "layout" tab for translations if the field is still translatable
  4. If there is access to the "layout" tab for translations puts message to the user instead of the form.
  5. First rough try at the message explaining the site state.

Status: Needs review » Needs work

The last submitted patch, 15: 3041659-15.patch, failed testing. View results

wim leers’s picture

Title: Remove the layout tab from translations because layout builder does not support translations » Remove the layout tab from translations because Layout Builder does not support translations
Issue summary: View changes
Related issues: +#3004536: Move the Layout Builder UI into an entity form for better integration with other content authoring modules and core features, +#2946333: Allow synced Layout override Translations: translating labels and inline blocks

Clarifying.

wim leers’s picture

ExpectationException: No link containing href /es/node/9/layout found

Looks like a "layout" tab is available when viewing entity translations. This appears to be a regression compared to #10.

+++ b/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
@@ -363,7 +363,10 @@ public function access($operation, AccountInterface $account = NULL, $return_as_
-    $result = $result->andIf(AccessResult::allowedIf(!($entity instanceof TranslatableInterface && !$entity->isDefaultTranslation())))->addCacheableDependency($entity);
+    $result = $result->andIf(AccessResult::allowedIf(
+      $entity->getFieldDefinition(OverridesSectionStorage::FIELD_NAME)->isTranslatable()
+      || !($entity instanceof TranslatableInterface && !$entity->isDefaultTranslation())));

I suspect this is the change that introduced that. I think it's missing a negation. Here's a patch to fix that.

EDIT: oh wait, I think that this is an intentional change, per #15.3 and this addition in #15:

+++ b/core/modules/layout_builder/src/Form/OverridesEntityForm.php
@@ -100,6 +101,12 @@ protected function init(FormStateInterface $form_state) {
   public function buildForm(array $form, FormStateInterface $form_state, SectionStorageInterface $section_storage = NULL) {
+    $entity = $this->getEntity();
+    if ($entity instanceof TranslatableInterface && !$entity->isDefaultTranslation()) {
+      return [
+        '#markup' => $this->t("Oh you would like to translate your layout? That's cool. I get it. Well but it's a little more complicated than that. So, well we tried. But you know there is translation and then localization and well it is really hard to know what you want. Also like what if you want both but maybe only sometimes. And what if you want to switch between one and the other on the same content. Or like what if you want to have some languages be translated and then some languages localized and on THE VERY SAME CONTENT. Like come on can't you see how this is really really complicated. And I haven't mentioned defaults and I wasn't going to mention them. But you know you are probably going to want to translate those and well maybe even localize them. Come on give us a break. But anyways we tried like I said. And you know we are going to do it and it is going to be awesome. So sorry you will just have to wait a little longer. Sorry."),
+      ];
+    }

— why this sudden change of direction tough? I don't see this mentioned in the issue summary or the comments here to do this?

tedbow’s picture

Issue summary: View changes

@Wim Leers sorry for not explaining. Updated the summary.

+++ b/core/modules/layout_builder/src/Form/OverridesEntityForm.php
@@ -100,6 +101,12 @@ protected function init(FormStateInterface $form_state) {
+        '#markup' => $this->t("Oh you would like to translate your layout? That's cool. I get it. Well but it's a little more complicated than that. So, well we tried. But you know there is translation and then localization and well it is really hard to know what you want. Also like what if you want both but maybe only sometimes. And what if you want to switch between one and the other on the same content. Or like what if you want to have some languages be translated and then some languages localized and on THE VERY SAME CONTENT. Like come on can't you see how this is really really complicated. And I haven't mentioned defaults and I wasn't going to mention them. But you know you are probably going to want to translate those and well maybe even localize them. Come on give us a break. But anyways we tried like I said. And you know we are going to do it and it is going to be awesome. So sorry you will just have to wait a little longer. Sorry."),

This was an attempt at humor, wit, facetiousness, jocularity 😜

tedbow’s picture

Status: Needs work » Needs review
StatusFileSize
new6.68 KB
new50.33 KB
  1. Fixed \Drupal\Tests\layout_builder\Kernel\OverridesSectionStorageTest::testAccess(). To do this I had to actually check $default_section_storage->isOverridable() in OverridesSectionStorage::access()otherwise the field would not be there.
  2. Change my long message to a short one and added a @todo to actually fix this.
  3. added a code comment about why we need this message
wim leers’s picture

+++ b/core/modules/layout_builder/src/Form/OverridesEntityForm.php
@@ -103,8 +103,14 @@ protected function init(FormStateInterface $form_state) {
+      // layout_builder_post_update_make_layout_untranslatable() could set it

"could NOT set it", right?

Let's also document how that can happen? Right now it sounds rather mysterious. Ominous even.

plach’s picture

Review of #15:

  1. +++ b/core/modules/layout_builder/layout_builder.post_update.php
    @@ -162,7 +167,88 @@ function layout_builder_post_update_section_third_party_settings_schema() {
    +  $a = 'b';
    

    mmh

  2. +++ b/core/modules/layout_builder/layout_builder.post_update.php
    @@ -162,7 +167,88 @@ function layout_builder_post_update_section_third_party_settings_schema() {
    +      if ($entity_type_has_translated_layouts === FALSE) {
    

    Nit: !$entity_type_has_translated_layouts?

  3. +++ b/core/modules/layout_builder/layout_builder.post_update.php
    @@ -162,7 +167,88 @@ function layout_builder_post_update_section_third_party_settings_schema() {
    + * Determines if there are no translated layout for a bundle.
    

    "translated layoutS"?

  4. +++ b/core/modules/layout_builder/layout_builder.post_update.php
    @@ -162,7 +167,88 @@ function layout_builder_post_update_section_third_party_settings_schema() {
    +        $join_condition = "d.$revision_key = f.revision_id";
    ...
    +        $join_condition = "d.{$entity_type->getKey('id')} = f.entity_id";
    

    We're missing a condition on the langcode column being the same (langcode is an entity key, so in the data table the actual column name might be different).

  5. +++ b/core/modules/layout_builder/layout_builder.post_update.php
    @@ -162,7 +167,88 @@ function layout_builder_post_update_section_third_party_settings_schema() {
    +        return FALSE;
    

    Shouldn't this be TRUE? If there is no data table the can't be any translation.

  6. +++ b/core/modules/layout_builder/layout_builder.post_update.php
    @@ -162,7 +167,88 @@ function layout_builder_post_update_section_third_party_settings_schema() {
    +      $select->condition('d.default_langcode', 0);
    

    default_langcode is an entity key.

  7. +++ b/core/modules/layout_builder/src/Form/OverridesEntityForm.php
    @@ -100,6 +101,12 @@ protected function init(FormStateInterface $form_state) {
    +        '#markup' => $this->t("Oh you would like to translate your layout? That's cool. I get it. Well but it's a little more complicated than that. So, well we tried. But you know there is translation and then localization and well it is really hard to know what you want. Also like what if you want both but maybe only sometimes. And what if you want to switch between one and the other on the same content. Or like what if you want to have some languages be translated and then some languages localized and on THE VERY SAME CONTENT. Like come on can't you see how this is really really complicated. And I haven't mentioned defaults and I wasn't going to mention them. But you know you are probably going to want to translate those and well maybe even localize them. Come on give us a break. But anyways we tried like I said. And you know we are going to do it and it is going to be awesome. So sorry you will just have to wait a little longer. Sorry."),
    

    We should add a dev-speak translation on localize.drupal.org so we don't lose this ;)

(#14 is still TODO)

plach’s picture

Title: Remove the layout tab from translations because Layout Builder does not support translations » Remove the layout tab from translations because Layout Builder does not support translations yet
tedbow’s picture

re #14 @plach thanks for the review.

  1. Sorry that was line made just to set a breakpoint. removed.
  2. fixed
  3. fixed
  4. added using key
  5. Ok fixed.
  6. fixed
  7. shorten it. But yes we will need to make a real message.

1 other change.

+++ b/core/modules/layout_builder/tests/src/Functional/Update/MakeLayoutUntranslatableUpdatePathTest.php
@@ -0,0 +1,110 @@
+    $page->pressButton('edit-submit');
+    $assert_session->linkByHrefNotExists(str_replace($this->baseUrl, '', $this->getSession()->getCurrentUrl()) . '/layout');

We actually want the layout link if there were translated values

so this patch passes fails but it passes if I revert #2554235: Make the content entity storage and entity query use the last installed definitions instead of the ones living in code. @plach you worked on that so hoping you my have some idea why.

If I print the test results this is the error on the node view.

Drupal\Core\Entity\Query\QueryException: '' not found in Drupal\Core\Entity\Query\Sql\Tables->ensureEntityTable() (line 367 of core/lib/Drupal/Core/Entity/Query/Sql/Tables.php).
Drupal\Core\Entity\Query\Sql\Tables->addField(, 'INNER', 'en') (Line: 52)
Drupal\Core\Entity\Query\Sql\Condition->compile(Object) (Line: 172)
Drupal\Core\Entity\Query\Sql\Query->compile() (Line: 80)
Drupal\Core\Entity\Query\Sql\Query->execute() (Line: 433)
Drupal\Core\Entity\ContentEntityStorageBase->getLatestTranslationAffectedRevisionId('1', 'en') (Line: 255)
Drupal\Core\Entity\EntityRepository->getLatestTranslationAffectedRevision(Object, 'en') (Line: 162)
Drupal\Core\Entity\EntityRepository->getActiveMultiple('node', Array, Array) (Line: 136)
Drupal\Core\Entity\EntityRepository->getActive('node', '1') (Line: 231)
Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage->extractEntityFromRoute('', Array) (Line: 192)
Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage->deriveContextsFromRoute('', Array, 'section_storage', Array) (Line: 55)
Drupal\layout_builder\Routing\LayoutTempstoreParamConverter->convert('', Array, 'section_storage', Array) (Line: 100)
Drupal\Core\ParamConverter\ParamConverterManager->convert(Array) (Line: 89)
Drupal\Core\Access\AccessManager->checkNamedRoute('layout_builder.overrides.node.view', Array, Object, 1) (Line: 347)
Drupal\Core\Menu\LocalTaskManager->getTasksBuild('entity.node.canonical', Object) (Line: 378)
Drupal\Core\Menu\LocalTaskManager->getLocalTasks('entity.node.canonical', 0) (Line: 95)
Drupal\Core\Menu\Plugin\Block\LocalTasksBlock->build() (Line: 163)
Drupal\block\BlockViewBuilder::preRender(Array)
call_user_func('Drupal\block\BlockViewBuilder::preRender', Array) (Line: 378)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 450)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 195)
Drupal\Core\Render\Renderer->render(Array) (Line: 501)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 116)
__TwigTemplate_6521f33a5dcdcf165de7ede6e56734581b58e0234c7e947d8ced5d735ca24156->doDisplay(Array, Array) (Line: 427)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 398)
Twig\Template->display(Array) (Line: 406)
Twig\Template->render(Array) (Line: 64)
twig_render_template('core/themes/bartik/templates/page.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('page', Array) (Line: 437)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 195)
Drupal\Core\Render\Renderer->render(Array) (Line: 501)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 102)
__TwigTemplate_06f1f6984f6e9b8f922b49865adaa55df027738f86cd121ca41b301d489d45a5->doDisplay(Array, Array) (Line: 427)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 398)
Twig\Template->display(Array) (Line: 406)
Twig\Template->render(Array) (Line: 64)
twig_render_template('core/themes/classy/templates/layout/html.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('html', Array) (Line: 437)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 195)
Drupal\Core\Render\Renderer->render(Array) (Line: 147)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 582)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 148)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 111)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch('kernel.view', Object) (Line: 156)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 68)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 106)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 85)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 50)
Drupal\ban\BanMiddleware->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 52)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 693)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

@tim.plunkett helped me debug this and he suggested in coming from \Drupal\Core\Entity\ContentEntityStorageBase::getLatestTranslationAffectedRevisionId()

->condition($this->entityType->getKey('id'), $entity_id)
        ->condition($this->entityType->getKey('revision_translation_affected'), 1, '=', $langcode)

Where 1 of these keys is returing. Guess it is revision_translation_affected because was added later in system_update_8402

Status: Needs review » Needs work

The last submitted patch, 24: 3041659-24.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

amateescu’s picture

StatusFileSize
new49.18 KB
new6.19 KB

I looked into the failing test and the problem is that system_update_8402() only added the base field definition but it didn't update the entity type definition as well to add the entity key. We need a separate issue to fix that..

While I was here, I cleaned up the update helper function and a few coding standards issues.

Not sending this patch to the testbot because it will have the same fails.

wim leers’s picture

Talked to @amateescu in Slack, he's opening an issue tonight to fix the problem he discovered in #26, when he's back at his laptop. He knows exactly what needs to change obviously, so having him do it would result in the clearest and hence easiest to commit issue+patch 🙂

tedbow’s picture

@amateescu thanks for looking into this problem!

+++ b/core/modules/layout_builder/layout_builder.post_update.php
@@ -167,3 +170,71 @@ function layout_builder_post_update_fix_tempstore_keys() {
+  $query = \Drupal::entityTypeManager()->getStorage($entity_type_id)->getQuery();
...
+    ->condition($entity_type->getKey('default_langcode'), 0)

Sorry I forget to mention this is a previous comment but pretty sure EntityQuery won't work in this case.

@plach and I spent time debugging this with a previous version that used an EntityQuery and the problem was it was not joining the tables with the 'langcode' key condition.

Basically if you have 2 rows in node_field_revision for the same revision because there is the default language and 1 translation. But in node_revision__layout_builder__layout you may only have 1 row for the revision_id because only the default language has a layout.

Without the join on langcode the query will still return 1 result even though there is condition on default_langcode = 0 which the row in node_revision__layout_builder__layout does not satisfy.

For layout builder you can create this situation in 8.6.x by creating a translation first and then creating a layout override for the default language.

I have tested manually to be sure I am correct but currently the fixture layout-builder-translation.php does not cover this case.

I will update that fixture to include another node of different content type where it has a translation but not a translated layout.

Then we will have 2 different content types 1 where the field should be left as translatable because there is translated layout and another where it can be safely set as non-translatable.

amateescu’s picture

Status: Needs work » Needs review
StatusFileSize
new52.17 KB

Here's the issue that fixes the missing revision_translation_affected entity key: #3042993: Translatable and revisonable installed entity type definitions are missing the 'revision_translation_affected' entity key, and a combined patch with #26.

I think the problem you described in #28 is tracked over here #2933202: EntityQuery returns incorrect results for notExists condition where one translation matches but others do not, I'll try to reproduce this scenario locally and see if we can tweak the entity query to work around that bug :)

tedbow’s picture

@amateescu thanks for creating the patch for #3042993: Translatable and revisonable installed entity type definitions are missing the 'revision_translation_affected' entity key!!!!!

This patch

  1. Reverts _layout_builder_no_translated_layouts() not use the entity query because of #2933202: EntityQuery returns incorrect results for notExists condition where one translation matches but others do not
  2. Changes the text fixtures to enable Layout Builder overrides for Basic Page content type also and set it up so there is only Layout override for the default language. This result in the page node not having the "layout" tab on translations
  3. Updated testDisableTranslationOnLayouts() to check Basic Page doesn't have the layouts tab.

The last submitted patch, 29: 3041659-29-combined.patch, failed testing. View results

The last submitted patch, 30: 3041659-translation-30-plus-3042993.patch, failed testing. View results

tedbow’s picture

StatusFileSize
new58.5 KB
new61.48 KB
new5.48 KB
  1. +++ b/core/modules/layout_builder/tests/src/Functional/Update/MakeLayoutUntranslatableUpdatePathTest.php
    @@ -0,0 +1,134 @@
    +    $node = $this->createNode(['title' => 'Default language', 'type' => $type]);
    ...
    +    $page->fillField('title[0][value]', 'Spanish translation');
    

    Trying to debug this by looking at printer output it was clear which bundle the page was on. Adding bundle the title.

  2. +++ b/core/modules/layout_builder/tests/src/Functional/Update/MakeLayoutUntranslatableUpdatePathTest.php
    @@ -0,0 +1,134 @@
    +    $this->drupalGet('es' . $node->toUrl()->toString());
    

    This fails in the subdirectory on Drupalci. Since we are still on the default translation at this point we can just click the links "Translate" and the the Spanish title

  3. +++ b/core/modules/layout_builder/tests/src/Functional/Update/MakeLayoutUntranslatableUpdatePathTest.php
    @@ -0,0 +1,134 @@
    +    $assert_session->pageTextContains('Custom block label on default language');
    

    We should not be checking this here as we checking it based on $translated_layout_expected below

plach’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/layout_builder/src/Form/OverridesEntityForm.php
    @@ -90,6 +91,18 @@ protected function init(FormStateInterface $form_state) {
    +        // @todo Create actual message to that they have a translated layout values.
    +        '#markup' => $this->t("Whoops we couldn't set field to non-translatable. Our bad."),
    

    Ok, we should really come up with something final now :)

    What about "Layout builder does not support layout localization, read the documentation for more details."?

  2. +++ b/core/modules/layout_builder/tests/fixtures/update/layout-builder-translation.php
    @@ -0,0 +1,168 @@
    +  $node_field_data['revision_translation_affected'] = NULL;
    ...
    +  $node_field_revision['revision_translation_affected'] = NULL;
    

    This need to be 1, see #14.

  3. +++ b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTranslationTest.php
    @@ -0,0 +1,217 @@
    +    $entity_url = $this->entity->toUrl('canonical')->toString();
    

    Nit: "canonical" is not needed here.

  4. +++ b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTranslationTest.php
    @@ -0,0 +1,217 @@
    +    // Create a translation.
    +    $this->drupalLogin($this->translator);
    

    Can we restore the previous user after creating the translation to perform a more consistent test?

  5. +++ b/core/modules/layout_builder/tests/src/Functional/Update/MakeLayoutUntranslatableUpdatePathTest.php
    @@ -0,0 +1,142 @@
    +    $this->assertEquals(
    +      $translated_layout_expected,
    +      FieldConfig::loadByName('node', $type, OverridesSectionStorage::FIELD_NAME)->isTranslatable(),
    +      $translated_layout_expected ? "Field on $type not set to translatable." : "Field on $type set to translatable."
    +    );
    

    Can we add an assertion also for the storage definition?

  6. +++ b/core/modules/layout_builder/tests/src/Functional/Update/MakeLayoutUntranslatableUpdatePathTest.php
    @@ -0,0 +1,142 @@
    +    $this->drupalGet("admin/structure/types/manage/$type/display");
    

    Is this needed?

  7. +++ b/core/modules/layout_builder/tests/src/Functional/Update/MakeLayoutUntranslatableUpdatePathTest.php
    @@ -0,0 +1,142 @@
    +    if ($translated_layout_expected) {
    +      $assert_session->linkByHrefExists($layout_href);
    

    Can we add an assertion to check that the warning message is displayed?

tedbow’s picture

Status: Needs work » Needs review
Issue tags: +Needs issue summary update
StatusFileSize
new9.84 KB
new58.39 KB

re #34

@plach thanks again for the review

  1. Ok. Used your suggestion except used "translations" instead of "localization". Is there a reason to choose "localization"? It just seems that is not in the UI elsewhere so it seems like it might be confusing to add it here.
  2. fixed
  3. removed and a couple unneeded instances.
  4. fixed. Also removed the // Create a translation. comment. because the name of the method is addEntityTranslation() and we have the docblock too.
  5. Right now the field storage would not be set to not translatable because if any bundles not set then the storage won't be set.
  6. Nope, removed
  7. Added the assertion for now but of course as soon as the message is added to the standard spanish translation won't this break? I also added an assertion that there are not inputs on the layout tab so we don't actually have the layout builder rendered.

Change to _layout_builder_no_translated_layouts()

I realized that to ensure that we don't change the output of existing sites entities and past revision we can't just check if there are entities with translated layouts.

Actually we have to check if there are any entities for the bundle that have both translations and layout overrides, regardless of whether the layout overrides are for the translations.

This is because in 8.6.x if you created a layout override for the default language after you created a translation then the translation would have no layout override translation(and couldn't make one). But this does not mean that entity view of the translation would use the default translations layout override. This actually means that because the layout override storage would have the translated entity and the field is translated then OverridesSectionStorage::isOverridden() would return false and the translation would use the bundle's default layout.

If we made the field untranslatable in this case then after the update the translation would go from showing the bundle's default layout to the layout override which would now apply to all languages.(it would be good if someone else could also manually confirm this)

I renamed _layout_builder_no_translated_layouts() to _layout_builder_no_entities_with_layouts_and_translations(). I changed the query to find any entities with both an override and a translation regardless of whether the layout override was for a translation. This is actually a slightly simpler query.

I also change the test assertion in MakeLayoutUntranslatableUpdatePathTest::testDisableTranslationOnLayouts() for the basic page content type from
$this->assertTranslatedLayoutWorkflow('page', FALSE);
to
$this->assertTranslatedLayoutWorkflow('page', TRUE);

Because I set up Basic Page content type in #33 to have a layout override for the default language and a translation(but no translated override). I thought this should mean the field for basic page should set non-translatable but as explained above it should not. So no actual changes need to the fixture.

plach’s picture

Status: Needs review » Needs work

@tedbow, #35:

1: Because different layouts per language is localization and not translation, but nbd: users might not be able to tell the difference.
5: Rrright
7: The string would be translated only if the test enabled the locale module, so asserting the English string should be fine.

  1. +++ b/core/modules/layout_builder/layout_builder.post_update.php
    @@ -233,24 +233,20 @@ function _layout_builder_no_translated_layouts($entity_type_id, $bundle) {
           $count = (int) $select->countQuery()->execute()->fetchField();
           return empty($count);
    

    I was wondering whether it wouldn't be better to switch to a LIMIT 0,1 ranged query to improve performance on very large data sets.

  2. +++ b/core/modules/layout_builder/src/Form/OverridesEntityForm.php
    @@ -99,8 +99,7 @@ public function buildForm(array $form, FormStateInterface $form_state, SectionSt
    +        '#markup' => $this->t("Layout builder does not support layout translations, read the <a href='https://www.drupal.org/node/3043164 >documentation</a> for more details."),
    

    Missing closing quote in the link tag, also we normally use double quotes for HTML attributes.

tedbow’s picture

Issue summary: View changes
StatusFileSize
new20.56 KB
new23.26 KB
new6.59 KB
new62.48 KB
  1. fixed
  2. fixed

Added

  1. A warning on the content translation settings form to warn about layout translations(we only be there for existing sites that were not able to set the storage to non-translatable.
    screenshot of warning
  2. Error on status page

    status page screenshot

plach’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 37: 3041659-37.patch, failed testing. View results

effulgentsia’s picture

I haven't reviewed the whole patch yet, but just some drive-by nits of the post_update:

  1. +++ b/core/modules/layout_builder/layout_builder.post_update.php
    @@ -167,3 +173,86 @@ function layout_builder_post_update_fix_tempstore_keys() {
    +    if (isset($field_infos['layout_builder__layout']['bundles'])) {
    +      foreach ($field_infos['layout_builder__layout']['bundles'] as $bundle) {
    +        if (_layout_builder_no_entities_with_layouts_and_translations($entity_type_id, $bundle)) {
    +          $field_config = FieldConfig::loadByName($entity_type_id, $bundle, OverridesSectionStorage::FIELD_NAME);
    

    Any reason not to use the OverridesSectionStorage::FIELD_NAME constant in the first two lines as well?

  2. +++ b/core/modules/layout_builder/layout_builder.post_update.php
    @@ -167,3 +173,86 @@ function layout_builder_post_update_fix_tempstore_keys() {
    + * Determines if there are no translated layouts for a bundle.
    ...
    + * @return bool
    + *   TRUE if there no translated layout, otherwise FALSE.
    + */
    +function _layout_builder_no_entities_with_layouts_and_translations($entity_type_id, $bundle) {
    

    If "translated layout" is an accurate description, per the docs, then can we also change the function name to _layout_builder_no_entities_with_translated_layouts()? I think the current name of "layouts_and_translations" is inaccurate, because I think a site can have entities which have both layout overrides (in the default language) and translations (without a translation of the layout), and for such a site, this function returns FALSE. Is that correct?

tedbow’s picture

Status: Needs work » Needs review
StatusFileSize
new2.93 KB
new62.58 KB

#37 failed because

+++ b/core/modules/layout_builder/tests/src/Functional/Update/MakeLayoutUntranslatableUpdatePathTest.php
@@ -0,0 +1,143 @@
+      $page->find('css', "[href=\"$layout_href\"]")->click();

This failed because of the test is in subdirectory

#40
@effulgentsia thanks for the review fixed.

  1. The logic has changed see #35 Change to _layout_builder_no_translated_layouts() section

    Actually we have to check if there are any entities for the bundle that have both translations and layout overrides, regardless of whether the layout overrides are for the translations.

  2. further description in the comment. But I didn't update the doc block when I change the function name. Updating.
tedbow’s picture

StatusFileSize
new62.46 KB

Just a reroll

tedbow’s picture

Issue summary: View changes
Issue tags: -Needs issue summary update
StatusFileSize
new1.17 KB
new62.87 KB
+++ b/core/modules/layout_builder/layout_builder.post_update.php
@@ -167,3 +173,87 @@ function layout_builder_post_update_fix_tempstore_keys() {
+      $select->innerJoin($field_table, 'f', "d.$id_key = f.entity_id");

I chatted with @effulgentsia and he pointed out that it is not super obvious why we are just joining on entity_id here and not langcode and/revision_id.

Adding a commented he suggested.

plach’s picture

+++ b/core/modules/layout_builder/layout_builder.module
@@ -174,10 +175,13 @@ function layout_builder_entity_build_defaults_alter(array &$build, EntityInterfa
+    // runs after content_moderation's alter so that it can add a warning to the

This is actually referring to Content Translation, right?

plach’s picture

StatusFileSize
new30.31 KB
  1. +++ b/core/modules/layout_builder/layout_builder.module
    @@ -350,3 +353,76 @@ function layout_builder_quickedit_render_field(EntityInterface $entity, $field_n
    +    // layout translations are not supported..
    

    Nit: double trailing dot

  2. +++ b/core/modules/layout_builder/layout_builder.module
    @@ -350,3 +353,76 @@ function layout_builder_quickedit_render_field(EntityInterface $entity, $field_n
    +          $layout_field_checkbox['#description'] = t('<strong>Warning:<strong> Layout Builder does not support layout translations, read the <a href="https://www.drupal.org/node/3043164" >documentation</a> for more details.');
    

    This is not great from a visual standpoint, what about appending it to the operation column instead and adding the messages messages--warning classes? See the screenshot below for a preview:

  3. +++ b/core/modules/layout_builder/layout_builder.module
    @@ -350,3 +353,76 @@ function layout_builder_quickedit_render_field(EntityInterface $entity, $field_n
    +        'description' => t('Layout Builder does not support layout translations, read the <a href="https://www.drupal.org/node/3043164" >documentation</a>. Layout translation support is currently enabled for the following. @bundle_issues', ['@bundle_issues' => $bundle_issues]),
    

    The last sentence seems truncated, I guess it meant to be something like:

    "the following items."

plach’s picture

+++ b/core/modules/layout_builder/layout_builder.module
@@ -350,3 +353,76 @@ function layout_builder_quickedit_render_field(EntityInterface $entity, $field_n
+          $field_config = FieldConfig::loadByName($entity_type_id, $bundle, OverridesSectionStorage::FIELD_NAME);

There is no corresponding use statement for FieldConfig, I was getting the following error on the status report:

Error: Class 'FieldConfig' not found in layout_builder_requirements() (line 404 of core/modules/layout_builder/layout_builder.module).

Also why not moving this to layout_builder.install?

plach’s picture

Status: Needs review » Needs work

NW for the last error.

effulgentsia’s picture

  1. +++ b/core/modules/layout_builder/layout_builder.post_update.php
    @@ -167,3 +173,93 @@ function layout_builder_post_update_fix_tempstore_keys() {
    +      // translation, nor do we care if it is for the same revision as the
    +      // revision that has a translation. Therefore, join only on the entity ID
    

    Has it already been discussed in this issue why we don't care about matching on revision? For example, if I had layout overrides before having translations. Then I stopped having layout overrides and only after that started having translations, then wouldn't it be safe to return TRUE? But if there are other scenarios where layout overrides and translations across different revisions are problematic, then yes, it would be safer to not join on revision ID. I'm just wondering if we know of such a scenario, or if we're just being extra cautious.

  2. +++ b/core/modules/layout_builder/layout_builder.post_update.php
    @@ -167,3 +173,93 @@ function layout_builder_post_update_fix_tempstore_keys() {
    +  // If we were not able to execute the query assume there are translated
    +  // layouts.
    +  return FALSE;
    

    s/translated layouts/translations and layouts/

    What about the case where the entity type or bundle isn't translatable? How are we handling that case in post_update? Or is there a reason that we don't have to?

effulgentsia’s picture

Re #35:

This is because in 8.6.x if you created a layout override for the default language after you created a translation then the translation would have no layout override translation(and couldn't make one). But this does not mean that entity view of the translation would use the default translations layout override. This actually means that because the layout override storage would have the translated entity and the field is translated then OverridesSectionStorage::isOverridden() would return false and the translation would use the bundle's default layout.

Good catch! So +1 to #41.1. I confirmed that this is the standard behavior for adding translatable fields to content that already has translations. For example, create a node with 2 translations, then add a field to that bundle, the go to /admin/config/regional/content-language and make the newly added field translatable. If you now edit the node in its default language and put some value into the new field, it only gets applied to the default language. The existing translation, when both viewed and edited, continues to have that new field be empty. Which is different than when you create a new translation, but I can see why this difference is desirable. Given all that, OverridesSectionStorage::isOverridden() is acting correctly (or at least, consistently with translatable fields in general), so you're right about our post_update needing to query for "layouts and translations" rather than "translated layouts".

tedbow’s picture

Status: Needs work » Needs review
StatusFileSize
new9.64 KB
new63.9 KB

self review.

  1. +++ b/core/modules/layout_builder/layout_builder.module
    @@ -350,3 +353,76 @@ function layout_builder_quickedit_render_field(EntityInterface $entity, $field_n
    +    // When creating a new translation do not copy untranslated section because
    

    s/section/sections

  2. +++ b/core/modules/layout_builder/layout_builder.module
    @@ -350,3 +353,76 @@ function layout_builder_quickedit_render_field(EntityInterface $entity, $field_n
    +    // layout translations are not supported..
    

    Two periods

  3. +++ b/core/modules/layout_builder/layout_builder.module
    @@ -350,3 +353,76 @@ function layout_builder_quickedit_render_field(EntityInterface $entity, $field_n
    +function layout_builder_requirements($phase) {
    

    This should be in the install file.

  4. +++ b/core/modules/layout_builder/layout_builder.post_update.php
    @@ -8,6 +8,12 @@
    +use Drupal\Core\Database\Query\Condition;
    

    unused

  5. +++ b/core/modules/layout_builder/src/Form/OverridesEntityForm.php
    @@ -90,6 +91,17 @@ protected function init(FormStateInterface $form_state) {
    +      // as untranslatable provide a message to the user about how to fix this
    +      // situation.
    

    We don't actually give them instructions here. Just link to documentation.

  6. +++ b/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
    @@ -362,6 +362,14 @@ public function save() {
    +      $result = $result->andIf(AccessResult::allowedIf(
    +        $entity->getFieldDefinition(OverridesSectionStorage::FIELD_NAME)->isTranslatable()
    +        || !($entity instanceof TranslatableInterface && !$entity->isDefaultTranslation())));
    +      $result->addCacheableDependency($entity);
    +    }
    

    Adding a comment here about why we are checking this access.

#44
fixed.

#45
fixed.

Stopping be back 30 minutes or so.

webchick’s picture

Soooo… @tim.plunkett, @tedbow, and @effulgentsia walked me through the current patch and… I got … very confused at the direction this patch has taken. I was sitting here believing this would be a 10-line patch to just remove the Layout tab from the non-default translation. :D

I know Layout Builder is beta, and therefore we care about the data integrity here… but this patch seems like it's going WAY over the top on hand-holding and putting permanent code in stable core for what I understand is being an issue that only affects a small portion of sites who used Layout Builder before 8.7.0-beta (when it was experimental) and are also multilingual.

It seems like we should be focusing on making sure this condition doesn't happen to new people (so do what the issue title says, and remove access to the tab), and making sure there's something easy to Google for those early adopters with multilingual sites who are suddenly missing a tab. Normally, it seems like we would just put an entry in the release notes that links to a site builder CR for a bug like this, versus codifying something in our UI for the rest of all of time for a temporary condition.

@tedbow had one concern, which was that if a site ever used Layout Builder before 8.7.0-beta, and makes it multilingual later, they will hit the same problem. For this, we could file a follow-up to add a (small) update path to make sure that if a site doesn't have any translations (apparently that's tricky to figure out), then mark the field untranslatable.

tedbow’s picture

StatusFileSize
new19.99 KB

Here is a patch that only

  1. Removes the tab for translations
  2. Adds layout_builder_entity_translation_create to ensure no new layout translations are created when you create a new translation
  3. Makes it sure that any new bundles that have overrides enable will not have their fields set to non-translatable.
tim.plunkett’s picture

Assigned: tim.plunkett » Unassigned

I completely agree with the proposal above in #51.

  1. +++ b/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
    @@ -362,6 +362,8 @@ public function save() {
    +    $result = $result->andIf(AccessResult::allowedIf(!($entity instanceof TranslatableInterface && !$entity->isDefaultTranslation())))->addCacheableDependency($entity);
    

    This deserves a code comment

  2. +++ /dev/null
    @@ -1,114 +0,0 @@
    -class LayoutBuilderMultilingualTest extends BrowserTestBase {
    

    Is there any value in keeping this test but changing the assertions to reflect the new reality? If not, this is fine

  3. +++ b/core/modules/layout_builder/tests/src/Kernel/OverridesSectionStorageTest.php
    @@ -79,14 +81,21 @@ public function testAccess($expected, $operation, $is_enabled, array $section_da
    +      $display
    +        ->setOverridable()
    +        ->save();
         }
    -    $display
    -      ->setOverridable()
    -      ->save();
    

    I don't quite remember why we made this change.
    But doesn't the ->save() still need to live outside the if()?

effulgentsia’s picture

So #51 is proposing to limit this patch to only:

  • Setting the layout $field and $field_storage to not translatable when overrides are newly enabled for bundles and entity types.
  • Removing the Layout tab from translations, even if it's for an entity type/bundle where the layout field is translatable (due to having been during 8.6).

It's proposing to not do any of:

  • Adding an update path to change people's existing layout fields to be untranslatable.
  • Adding a status report error/warning for those people.
  • Adding a warning on the "Layout" row of the content translation configuration page for those people.
  • Retaining the Layout tab from translations for those people, and having it tell you it doesn't work.

I agree with punting on the update path. It's getting too close to beta, and we're still having problems with it. I'd rather provide it when we're confident about it, whether that's for RC, in a patch release, in 8.8, or in a contrib module. Although https://www.drupal.org/core/experimental#beta says that beta module provide update paths, we're dealing with a case where this patch isn't breaking anything by lack of an update path. It's leaving an existing site functioning in the same way that it already was. And someone who wants the better behavior, they can go to the content translation configuration page and uncheck the box.

FWIW, I'm also ok with punting on the UI warnings, especially if a product manager doesn't think they're necessary.

plach’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

Totally +1 on #51 as well, I had the same feeling that this was going too far: the scenarios we were accounting for feel way distant from the desired translation support, at least according from the feedback we were able to gather so far. For that reason I doubt there are many sites that are intentionally relying on the current behavior. Those can alter the tab access control and translation creation logic to restore it, we can even add some code snippets to the CR to explain how to do that.

For sites actually being hurt by this behavior, the following workaround is definitely viable:

And someone who wants the better behavior, they can go to the content translation configuration page and uncheck the box.

And if in a follow-up we can provide an update to make fields untranslatable where there's no risk of changing the site behavior unexpectedly, all the better.


@tim.plunkett, #53:

I'm not sure it's worth keeping that test around: the current logic is all covered by LayoutBuilderTranslationTest, at least from a functional perspective. I'd rather add a small kernel test to cover layout_builder_entity_translation_create(). Other than that and the remaining bullets in #53, the patch seems ready to go to me (reroll aside).

tedbow’s picture

Status: Needs work » Needs review
StatusFileSize
new4 KB
new24.61 KB
new22.52 KB

rerolled

#53

  1. added comment.
  2. Agree with @plach no need for this test.
    Added a test for translation creation. The test actually passes even without layout_builder_entity_translation_create() so I am not sure what is going on . I will update a another patch that should fail but doesn't. Search that patch for "WHY CAN THIS BE DELETED AND TEST STILL PASS??????"
  3. change the test in the reroll and this is not needed. In testAccess now every time it is called it checks an entity translation to make sure it denies access.
tedbow’s picture

StatusFileSize
new4.03 KB
new22.55 KB
new24.64 KB

TEST GROUP!!!

tim.plunkett’s picture

@plach pointed out that the second param of addTranslation is the key here: in the UI workflow the entity values of the default translation are passed in. Doing that here makes the test work as expected.

FAIL patch comments out the one line in layout_builder_entity_translation_create()

plach’s picture

This looks great to me!

I have a couple of very minor non-blocking remarks that it would be nice to address if we happen to reroll this patch:

  1. +++ b/core/modules/layout_builder/tests/src/Kernel/TranslatableFieldTest.php
    @@ -0,0 +1,86 @@
    +    // Create a translation.
    +    ConfigurableLanguage::createFromLangcode('es')->save();
    +
    +    LayoutBuilderEntityViewDisplay::create([
    +      'targetEntityType' => 'entity_test',
    +      'bundle' => 'entity_test',
    +      'mode' => 'default',
    +      'status' => TRUE,
    +    ])
    +      ->enableLayoutBuilder()
    +      ->setOverridable()
    +      ->save();
    +
    +    FieldStorageConfig::loadByName('entity_test', OverridesSectionStorage::FIELD_NAME)
    +      ->setTranslatable(TRUE)
    +      ->save();
    +    FieldConfig::loadByName('entity_test', 'entity_test', OverridesSectionStorage::FIELD_NAME)
    +      ->setTranslatable(TRUE)
    +      ->save();
    

    I'd move this logic to the ::setUp() method.

  2. +++ b/core/modules/layout_builder/tests/src/Kernel/TranslatableFieldTest.php
    @@ -0,0 +1,86 @@
    +    // Layout translations are not supported.
    

    This is testing that "Per-language layouts are not supported". I think what we mean with layout translation is translating the layout component textual elements.

plach’s picture

Also, we need a few updates to reflect the latest change in direction.

tim.plunkett’s picture

Issue tags: +Needs release note
StatusFileSize
new21.42 KB
new2.48 KB

Fixed those, and applied the same change as 2 on the docs for our hook.

Also we need an addition for the release note.

tim.plunkett’s picture

In order for custom code to be able to restore translation functionality, splitting the access check out to a protected method.

The last submitted patch, 58: 3041659-translation-58-FAIL.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

plach’s picture

Ok, aside from the pending updates this looks RTBC to me.

tedbow’s picture

effulgentsia’s picture

Issue summary: View changes
Issue tags: -Needs release note +8.7.0 release notes

Added a release note snippet to the IS.

tim.plunkett’s picture

Status: Needs review » Reviewed & tested by the community

plach signed off in #64, pending the IS/CR/RN updates.
Confirming that this is now RTBC.
Thanks!

plach’s picture

The IS and snippet look good to me, I performed a couple of small tweaks to the CR.

RTBC +1

xjm’s picture

Priority: Normal » Major
xjm’s picture

Issue tags: +8.7.0 upgrade path

OK I'm really concerned that the update path was abandoned here. We're knowingly deleting site data for sites that unknowingly started a translation before, with no workaround or other option for those sites to preserve their data.

xjm’s picture

The release note says:

Layout Builder overrides are now stored in non-translatable fields. This means that on entities with overridable layouts, the Layout tab is only available when viewing the entity in its default language (not from a translation) and layout changes made from there apply to all translations. For sites that installed Layout Builder and enabled layout overrides for some entity bundles while it was still Experimental (prior to 8.7.0-beta1), this release retains the site's existing translatability configuration of those fields. See the change record for more details.

Is that still true? It contradicts the patch and recent discussion in the issue. Site owners need to know the impact this will have and data loss/orphaned data is a pretty serious disruption.

xjm’s picture

For me, whatever followup issue to fix existing sites is a stable blocker. I've reached out to catch for a second opinion.

xjm’s picture

Status: Reviewed & tested by the community » Needs work

After reading the CR carefully and discussing with @tedbow and @effulgentsia, I'm really concerned with this part:

For sites that enable Content Translation at a later time
Sites that had layout overrides enabled before 8.7.0 and some time later enable the Content Translation module will also need to take the same action above to get the current module functionality for overrides

I asked @effulgentsia if this meant what I thought it did, and he confirmed:

Unfortunately, it also applies to if they use CT later. So for example, if they added LB in 8.6 and enabled layout overrides for articles. And they ignore this CR cause their site is monolingual. And then in a year they install CT. Then they'll have this same broken behavior at that time (for articles). Yesterday, Ted suggested an update path that at the very least handles this case (i.e., set the fields to not translatable if the site is currently monolingual). I think we could get that written pretty quickly, depending on how easy or hard it is to get the right test fixtures in place for it.

To me this is very much a data integrity issue. There is no way that sites that started using Layout Builder in 8.6 are going to find this one buried bullet point in the 8.7.0 release notes when they decide to enable Content Translation next year. There's no workaround, no action they can take now. We need either in-context messages about this to the user in the UI, or an upgrade path to fix it.

effulgentsia’s picture

What about committing this patch as-is, and making #73 a stable-blocking follow-up? The patch here doesn't regress anything. Maybe it's just not enough to mark the module stable yet?

xjm’s picture

Issue tags: +Needs followup

Ted said he would file a separate issue for the update path. For me, an update path to address at least #73 (update the field on monolingual sites) might be stable-blocking. @effulgentsia also mentioned that part of the problem has been upgrade path tests, but for a data integrity issue like this, I would prefer a thorougly manually tested upgrade path with tests descoped to a critical followup, rather than no upgrade path at all. But we can discuss in the other issue if it does get split out.

Crossposted with #74. I think one of two things is stable blocking: UI messages to the user about what happens when this field is marked translatable, or an upgrade path. I'd be okay with followup issues for both, tagged as stable blockers, but that we could decide to descope following discussion or if one or the other lands.

tim.plunkett’s picture

xjm’s picture

Issue summary: View changes
xjm’s picture

xjm’s picture

Issue summary: View changes
Status: Needs work » Reviewed & tested by the community

I updated the release note to be a bit clearer about not recommending the old behavior, and worked on an update of the CR with @effulgentsia.

@tedbow, @tim.plunkett, @effulgentsia and I discussed #73 and agreed that this can go in on its own, since it mostly solves the data integrity problem for sites that haven't already enabled layout overrides somewhere. However, the two followups #3043646: For sites that have made layout overrides prior to 8.7.0 or sites that manually enable translation of the layout override field, add UI warnings and #3043651: Update the Layout field to non-translatable when possible. remain as stable blockers for the time being, until we can decide how to handle existing overrides as well as sites that might enable translation on the field in the future.

xjm’s picture

Issue tags: -Needs followup
tim.plunkett’s picture

effulgentsia’s picture

Crediting reviewers.

  • effulgentsia committed cbf7788 on 8.8.x
    Issue #3041659 by tedbow, tim.plunkett, amateescu, plach, xjm,...

  • effulgentsia committed 3593825 on 8.7.x
    Issue #3041659 by tedbow, tim.plunkett, amateescu, plach, xjm,...
effulgentsia’s picture

Version: 8.8.x-dev » 8.7.x-dev
Status: Reviewed & tested by the community » Fixed

Pushed to 8.8.x and 8.7.x and published the CR. See you in the two followups from #79.

plach’s picture

+1 on #79, sounds like a very reasonable way forward, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

plach’s picture

Issue tags: -8.7.0 upgrade path +8.7.0 update