We have a content type called "Blog".

You can see that it has the correct field for "layout_builder__layout".

before anything, it has the correct field.

This content type has the "Default" display mode set to use Layout at `/admin/structure/types/manage/blog/display`

Default display uses layout

We create a new display mode called "Blog Card" at `/admin/structure/display-modes/view/add/node`

In "Manage Display" of the content type "Blog" we add the Display mode we just created.

We added the "Blog card" display mode to the "blog" content type

This starts as using Layout to manage display.

The new display mode uses Layout by default.

On the new display mode, "Blog Card" disable Layout at `/admin/structure/types/manage/blog/display/blog_card`

Go back to "manage fields" and you see that the "layout_builder__layout" field is missing.

Missing field on manage fields after disabling LAYOUT on new display mode.

Creating a new Blog node results in a nasty error message :

The website encountered an unexpected error. Please try again later.</br></br><em class="placeholder">InvalidArgumentException</em>: Field layout_builder__layout is unknown. in <em class="placeholder">Drupal\Core\Entity\ContentEntityBase-&gt;getTranslatedField()</em> (line <em class="placeholder">586</em> of <em class="placeholder">core/lib/Drupal/Core/Entity/ContentEntityBase.php</em>). <pre class="backtrace">Drupal\Core\Entity\ContentEntityBase-&gt;get(&#039;layout_builder__layout&#039;) (Line: 242)
Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay-&gt;getRuntimeSections(Object) (Line: 207)
Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay-&gt;buildMultiple(Array) (Line: 321)
Drupal\Core\Entity\EntityViewBuilder-&gt;buildComponents(Array, Array, Array, &#039;full&#039;) (Line: 22)
Drupal\node\NodeViewBuilder-&gt;buildComponents(Array, Array, Array, &#039;full&#039;) (Line: 263)
Drupal\Core\Entity\EntityViewBuilder-&gt;buildMultiple(Array) (Line: 220)
Drupal\Core\Entity\EntityViewBuilder-&gt;build(Array)
call_user_func(Array, Array) (Line: 378)
Drupal\Core\Render\Renderer-&gt;doRender(Array, ) (Line: 195)
Drupal\Core\Render\Renderer-&gt;render(Array, ) (Line: 226)
Drupal\Core\Render\MainContent\HtmlRenderer-&gt;Drupal\Core\Render\MainContent\{closure}() (Line: 582)
Drupal\Core\Render\Renderer-&gt;executeInRenderContext(Object, Object) (Line: 227)
Drupal\Core\Render\MainContent\HtmlRenderer-&gt;prepare(Array, Object, Object) (Line: 117)
Drupal\Core\Render\MainContent\HtmlRenderer-&gt;renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber-&gt;onViewRenderArray(Object, &#039;kernel.view&#039;, Object)
call_user_func(Array, Object, &#039;kernel.view&#039;, Object) (Line: 111)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher-&gt;dispatch(&#039;kernel.view&#039;, Object) (Line: 156)
Symfony\Component\HttpKernel\HttpKernel-&gt;handleRaw(Object, 1) (Line: 68)
Symfony\Component\HttpKernel\HttpKernel-&gt;handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session-&gt;handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle-&gt;handle(Object, 1, 1) (Line: 99)
Drupal\page_cache\StackMiddleware\PageCache-&gt;pass(Object, 1, 1) (Line: 78)
Drupal\page_cache\StackMiddleware\PageCache-&gt;handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware-&gt;handle(Object, 1, 1) (Line: 52)
Drupal\Core\StackMiddleware\NegotiationMiddleware-&gt;handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel-&gt;handle(Object, 1, 1) (Line: 665)
Drupal\Core\DrupalKernel-&gt;handle(Object) (Line: 19)
</pre>

The important part I think :

The website encountered an unexpected error. Please try again later.InvalidArgumentException: Field layout_builder__layout is unknown.

Indicating the field is missing.

Comments

alphex created an issue. See original summary.

tim.plunkett’s picture

Version: 8.6.0-beta2 » 8.7.x-dev
Assigned: Unassigned » tim.plunkett
Priority: Normal » Major
Issue tags: +Blocks-Layouts

I'm facepalming VERY hard over here. Thanks for filing this!

tim.plunkett’s picture

Status: Active » Needs review
Issue tags: +sprint
StatusFileSize
new3.23 KB
new6.7 KB

This is partially due to how creating new view modes works: it copies 100% of the values over from default.
Even values that currently don't make sense, like enabling overrides for other view modes.
Until #2907413: Consider supporting Layout Builder Overrides for other view modes happens, that shouldn't be possible...

But let's futureproof this anyway.
The field should not be removed until no other view modes of that bundle have overrides enabled.
Not sure if there's a better way to do that than this entity query, but it should work.

The last submitted patch, 3: 2990517-field-3-FAIL.patch, failed testing. View results

tim.plunkett’s picture

Assigned: tim.plunkett » Unassigned

Love getting it red/green on the first try.

neclimdul’s picture

Wanted to just RTBC since its nice and clean but....

+++ b/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
@@ -139,6 +139,28 @@ public function preSave(EntityStorageInterface $storage) {
+  protected function removeSectionField($entity_type_id, $bundle, $field_name) {
+    $query = $this->entityTypeManager()->getStorage($this->entityTypeId)->getQuery()
+      ->condition('targetEntityType', $this->getTargetEntityTypeId())
+      ->condition('bundle', $this->getTargetBundle())
+      ->condition('mode', $this->getMode(), '<>')
+      ->condition('third_party_settings.layout_builder.allow_custom', TRUE);
+    $enabled = (bool) $query->count()->execute();
+    if (!$enabled && $field = FieldConfig::loadByName($entity_type_id, $bundle, $field_name)) {

Hard computer problem, naming things. Since this conditionally removes instead of always removes, should it maybe be "cleanSectionField()" or "pruneSectionField()"?

tim.plunkett’s picture

Issue tags: +MWDS2018

Tagging to work on this next week.

sugaroverflow’s picture

Assigned: Unassigned » sugaroverflow

reviewing at MWDS

xjm’s picture

Priority: Major » Critical

This sounds critical. Is there a mitigation I'm missing? I don't quite follow #3.

tim.plunkett’s picture

Assigned: sugaroverflow » Unassigned
StatusFileSize
new5.03 KB

#3 wasn't mitigation or anything, just explaining how we ended up here.

Rerolled after #2998215: PHP notices/warnings when managing display for non-default view mode when LB is enabled w/ overrides

tim.plunkett’s picture

phenaproxima’s picture

Looks pretty good to me...

  1. +++ b/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
    @@ -139,6 +139,28 @@ public function preSave(EntityStorageInterface $storage) {
    +  protected function removeSectionField($entity_type_id, $bundle, $field_name) {
    

    Minor point, but does this really need to take any parameters? The $entity_type_id, $bundle, and $field_name are all known to the display entity already.

    Eh, I guess this is a nitpick, really, seeing as how addSectionField() follows the same pattern. Just pointing it out.

  2. +++ b/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
    @@ -139,6 +139,28 @@ public function preSave(EntityStorageInterface $storage) {
    +    $query = $this->entityTypeManager()->getStorage($this->entityTypeId)->getQuery()
    

    Can we use $this->getEntityTypeId() instead of $this->entityTypeId?

phenaproxima’s picture

Status: Needs review » Needs work
+++ b/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
@@ -139,6 +139,28 @@ public function preSave(EntityStorageInterface $storage) {
+  /**
+   * Removes a layout section field from a given bundle.

I echo @neclimdul's sentiment about either renaming this method, or changing the doc comment to explain that the field *may* not be removed.

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new1.32 KB
new5.17 KB

#12.1
Yeah, this is done for parallelism with addSectionField()

#12.2
Fixed

#13.2
Adjusted/expanded the docblock

phenaproxima’s picture

Status: Needs review » Reviewed & tested by the community

Thanks, @tim.plunkett. This looks excellent. RTBC on green.

xjm’s picture

Wow, I think this is the most critical non-security bug I've seen since... maybe since release. Didn't even realize the full impact when I skimmed it before. Change a display mode and break all your site's content!

I encountered #2289551: Clarify what 'locked' means for a config entity and whether it's okay for code to rely on a locked config entity existing and (I think) #2941189: Confirmation form for disabling per-entity layout overrides shows a success message and should not in the process of testing this; just adding them to the related issues for posterity.

I also noticed that the confirm title and description are confusing:

The title should actually be something like "Are you sure you want to disable Layout Builder for the Teaser display?" or perhaps simply "Are you sure you want to disable this layout?" The description should be something like "This will remove all your layout data for the Teaser display of Article content". That exists in HEAD, though

Normally I'd want the missing followup filed prior to committing the patch, but that bug is there in HEAD too and this issue is bad enough that I don't want to block it on anything. Will probably commit this once I finish reviewing the test.

xjm’s picture

StatusFileSize
new73.42 KB
xjm’s picture

  1. +++ b/core/modules/layout_builder/tests/src/Functional/LayoutDisplayTest.php
    @@ -0,0 +1,94 @@
    +    $this->drupalLogin($this->drupalCreateUser([
    +      'configure any layout',
    +      'administer node display',
    +      'administer display modes',
    +    ], 'foobar'));
    

    foobar isn't the best test content; I actually had to look up the second argument of drupalCreateUser() to figure it out.

    Spoiler: It's just the username. I don't think we actually need it here as it's an optional argument (random in the 99% case where it's not specified) and we don't use it in this test? Not really a big deal.

  2. +++ b/core/modules/layout_builder/tests/src/Functional/LayoutDisplayTest.php
    @@ -0,0 +1,94 @@
    +    // Enable Layout Builder for the default view modes, and overrides.
    

    The comma here isn't grammatically feasible.

  3. +++ b/core/modules/layout_builder/tests/src/Functional/LayoutDisplayTest.php
    @@ -0,0 +1,94 @@
    +    $assert_session->pageTextNotContains('Powered by Drupal');
    ...
    +    $assert_session->pageTextContains('Powered by Drupal');
    

    Even with the pageTextNotContains(), is this specific enough? E.g. we might change the flow and this could match the sidebar link or something. OTOH this isn't what we're actually testing in this test, so it's probably good enough.

  4. +++ b/core/modules/layout_builder/tests/src/Functional/LayoutDisplayTest.php
    @@ -0,0 +1,94 @@
    +    $assert_session->linkExists('Layout');
    +    $this->clickLink('Layout');
    

    At first this looked redundant; one would think clickLink() would account for the assertion preceding it. But then I remembered that (IIRC) clicking a link that doesn't exist gives some incomprehensible failure message, so this likely makes sense.

These are NBD and are potentially copied from other LB tests, so I don't think they need to be blocking (for the same reasons as in #16).

  • xjm committed 2227375 on 8.7.x
    Issue #2990517 by tim.plunkett, alphex, xjm, phenaproxima, neclimdul:...

  • xjm committed 953a117 on 8.6.x
    Issue #2990517 by tim.plunkett, alphex, xjm, phenaproxima, neclimdul:...
xjm’s picture

Status: Reviewed & tested by the community » Fixed

Committed and pushed to 8.7.x and 8.6.x. Thanks!

Followups for #16 and #18 would be nice.

xjm’s picture

Status: Fixed » Closed (fixed)

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

stancsmith@gmail.com’s picture

When attempting to patch 8.7.3, I got the following error from composer. Any suggestions?

patch '-p1' --no-backup-if-mismatch -d 'web/core' < '/var/www/docker_platform/web/patches/2990517-field-14.patch'
Executing command (CWD): patch '-p1' --no-backup-if-mismatch -d 'web/core' < '/var/www/docker_platform/web/patches/2990517-field-14.patch'
can't find file to patch at input line 5
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|diff --git a/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php b/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
|index 78ff430b00..2bdeb03870 100644
|--- a/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
|+++ b/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
--------------------------
File to patch:
Skip this patch? [y]
Skipping patch.
2 out of 2 hunks ignored
patching file core/modules/layout_builder/tests/src/Functional/LayoutDisplayTest.php

patch '-p0' --no-backup-if-mismatch -d 'web/core' < '/var/www/docker_platform/web/patches/2990517-field-14.patch'
Executing command (CWD): patch '-p0' --no-backup-if-mismatch -d 'web/core' < '/var/www/docker_platform/web/patches/2990517-field-14.patch'
can't find file to patch at input line 5
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|diff --git a/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php b/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
|index 78ff430b00..2bdeb03870 100644
|--- a/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
|+++ b/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
--------------------------
File to patch:
Skip this patch? [y]
Skipping patch.

2 out of 2 hunks ignored

patching file b/core/modules/layout_builder/tests/src/Functional/LayoutDisplayTest.php

patch '-p2' --no-backup-if-mismatch -d 'web/core' < '/var/www/docker_platform/web/patches/2990517-field-14.patch'
Executing command (CWD): patch '-p2' --no-backup-if-mismatch -d 'web/core' < '/var/www/docker_platform/web/patches/2990517-field-14.patch'
patching file modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php

Hunk #1 FAILED at 112.

Hunk #2 succeeded at 184 (offset 45 lines).

1 out of 2 hunks FAILED
 -- saving rejects to file modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php.rej

The next patch would create the file modules/layout_builder/tests/src/Functional/LayoutDisplayTest.php,
which already exists!  Assume -R? [n]
Apply anyway? [n]
Skipping patch.

1 out of 1 hunk ignored

patch '-p4' --no-backup-if-mismatch -d 'web/core' < '/var/www/docker_platform/web/patches/2990517-field-14.patch'
Executing command (CWD): patch '-p4' --no-backup-if-mismatch -d 'web/core' < '/var/www/docker_platform/web/patches/2990517-field-14.patch'
can't find file to patch at input line 5
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|diff --git a/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php b/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
|index 78ff430b00..2bdeb03870 100644
|--- a/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
|+++ b/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
--------------------------
File to patch:
Skip this patch? [y]
Skipping patch.

2 out of 2 hunks ignored

patching file tests/src/Functional/LayoutDisplayTest.php

   Could not apply patch! Skipping. The error was: Cannot apply patch web/patches/2990517-field-14.patch

The same patch process created LayoutBuilderEntityViewDisplay.php.rej with this:

--- modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
+++ modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
@@ -112,8 +112,8 @@ public function preSave(EntityStorageInterface $storage) {
       if ($new_value) {
         $this->addSectionField($entity_type_id, $bundle, 'layout_builder__layout');
       }
-      elseif ($field = FieldConfig::loadByName($entity_type_id, $bundle, 'layout_builder__layout')) {
-        $field->delete();
+      else {
+        $this->removeSectionField($entity_type_id, $bundle, 'layout_builder__layout');
       }
     }

Here is the relevant section from composer.json:

"extra": {
		 "enable-patching": true,
		 "patches": {
		 	 "drupal/core": {
                   "Contextual filter patch": "web/patches/2710407-14-validator-transform-fix.patch",
                   "Layout Builder missing field patch": "web/patches/2990517-field-14.patch"
		 	 },