From 223216cb94d8ddcae3920f8844c09fe4f0f63fd1 Mon Sep 17 00:00:00 2001
From: Lee Rowlands <lee.rowlands@previousnext.com.au>
Date: Mon, 30 Apr 2018 10:33:47 +1000
Subject: [PATCH] Issue #2956202 by tim.plunkett, tedbow, amateescu, xjm:
 EntityDisplayBase::init() should use ::setComponent() for extra fields

---
 .../Drupal/Core/Entity/EntityDisplayBase.php  |  6 ++--
 .../tests/src/Kernel/EntityDisplayTest.php    | 10 +++++-
 .../EntityViewDisplayResourceTestBase.php     |  2 ++
 core/modules/system/system.post_update.php    | 31 +++++++++++++++++++
 .../Update/UpdateEntityDisplayTest.php        | 24 ++++++++++++--
 5 files changed, 67 insertions(+), 6 deletions(-)

diff --git a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php
index 34ce858b13dc..5c3788761ee6 100644
--- a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php
@@ -163,12 +163,12 @@ protected function init() {
         if (!isset($this->content[$name]) && !isset($this->hidden[$name])) {
           // Extra fields are visible by default unless they explicitly say so.
           if (!isset($definition['visible']) || $definition['visible'] == TRUE) {
-            $this->content[$name] = [
+            $this->setComponent($name, [
               'weight' => $definition['weight']
-            ];
+            ]);
           }
           else {
-            $this->hidden[$name] = TRUE;
+            $this->removeComponent($name);
           }
         }
         // Ensure extra fields have a 'region'.
diff --git a/core/modules/field_ui/tests/src/Kernel/EntityDisplayTest.php b/core/modules/field_ui/tests/src/Kernel/EntityDisplayTest.php
index 2d02e44a5585..c6a7a3f156c4 100644
--- a/core/modules/field_ui/tests/src/Kernel/EntityDisplayTest.php
+++ b/core/modules/field_ui/tests/src/Kernel/EntityDisplayTest.php
@@ -166,7 +166,15 @@ public function testExtraFieldComponent() {
 
     // Check that the default visibility taken into account for extra fields
     // unknown in the display.
-    $this->assertEqual($display->getComponent('display_extra_field'), ['weight' => 5, 'region' => 'content']);
+    $this->assertEqual(
+      $display->getComponent('display_extra_field'),
+      [
+        'weight' => 5,
+        'region' => 'content',
+        'settings' => [],
+        'third_party_settings' => [],
+      ]
+    );
     $this->assertNull($display->getComponent('display_extra_field_hidden'));
 
     // Check that setting explicit options overrides the defaults.
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityViewDisplay/EntityViewDisplayResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityViewDisplay/EntityViewDisplayResourceTestBase.php
index ccc3aad3c02b..3278e4157cc1 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityViewDisplay/EntityViewDisplayResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityViewDisplay/EntityViewDisplayResourceTestBase.php
@@ -68,6 +68,8 @@ protected function getExpectedNormalizedEntity() {
         'links' => [
           'region' => 'content',
           'weight' => 100,
+          'settings' => [],
+          'third_party_settings' => [],
         ],
       ],
       'dependencies' => [
diff --git a/core/modules/system/system.post_update.php b/core/modules/system/system.post_update.php
index 229e309edd3c..038d4b82909b 100644
--- a/core/modules/system/system.post_update.php
+++ b/core/modules/system/system.post_update.php
@@ -5,7 +5,9 @@
  * Post update functions for System.
  */
 
+use Drupal\Core\Config\Entity\ConfigEntityUpdater;
 use Drupal\Core\Entity\Display\EntityDisplayInterface;
+use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
 use Drupal\Core\Entity\Entity\EntityFormDisplay;
 use Drupal\Core\Entity\Entity\EntityViewDisplay;
 
@@ -139,3 +141,32 @@ function system_post_update_change_delete_action_plugins() {
 function system_post_update_language_item_callback() {
   // Empty post-update hook.
 }
+
+/**
+ * Update all entity displays that contain extra fields.
+ */
+function system_post_update_extra_fields(&$sandbox = NULL) {
+  $config_entity_updater = \Drupal::classResolver(ConfigEntityUpdater::class);
+  $entity_field_manager = \Drupal::service('entity_field.manager');
+
+  $callback = function (EntityDisplayInterface $display) use ($entity_field_manager) {
+    $display_context = $display instanceof EntityViewDisplayInterface ? 'display' : 'form';
+    $extra_fields = $entity_field_manager->getExtraFields($display->getTargetEntityTypeId(), $display->getTargetBundle());
+
+    // If any extra fields are used as a component, resave the display with the
+    // updated component information.
+    $needs_save = FALSE;
+    if (!empty($extra_fields[$display_context])) {
+      foreach ($extra_fields[$display_context] as $name => $extra_field) {
+        if ($component = $display->getComponent($name)) {
+          $display->setComponent($name, $component);
+          $needs_save = TRUE;
+        }
+      }
+    }
+    return $needs_save;
+  };
+
+  $config_entity_updater->update($sandbox, 'entity_form_display', $callback);
+  $config_entity_updater->update($sandbox, 'entity_view_display', $callback);
+}
diff --git a/core/modules/system/tests/src/Functional/Update/UpdateEntityDisplayTest.php b/core/modules/system/tests/src/Functional/Update/UpdateEntityDisplayTest.php
index 698d331099cb..e7bdcd55ce22 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdateEntityDisplayTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdateEntityDisplayTest.php
@@ -7,7 +7,7 @@
 use Drupal\FunctionalTests\Update\UpdatePathTestBase;
 
 /**
- * Tests system_post_update_add_region_to_entity_displays().
+ * Tests updates for entity displays.
  *
  * @group Update
  */
@@ -24,8 +24,10 @@ protected function setDatabaseDumpFiles() {
 
   /**
    * Tests that entity displays are updated with regions for their fields.
+   *
+   * @see system_post_update_add_region_to_entity_displays()
    */
-  public function testUpdate() {
+  public function testRegionUpdate() {
     // No region key appears pre-update.
     $entity_form_display = EntityFormDisplay::load('node.article.default');
     $options = $entity_form_display->getComponent('body');
@@ -47,4 +49,22 @@ public function testUpdate() {
     $this->assertIdentical('content', $options['region']);
   }
 
+  /**
+   * Tests that entity displays are updated to properly store extra fields.
+   *
+   * @see system_post_update_extra_fields()
+   */
+  public function testExtraFieldsUpdate() {
+    $assertion = function ($expected_keys) {
+      $entity_view_display = EntityViewDisplay::load('node.article.default');
+      $this->assertEquals($expected_keys, array_keys($entity_view_display->getComponent('links')));
+    };
+
+    // Before the update extra fields are missing additional configuration.
+    $assertion(['weight', 'region']);
+    $this->runUpdates();
+    // After the update the additional configuration is present.
+    $assertion(['weight', 'region', 'settings', 'third_party_settings']);
+  }
+
 }
-- 
GitLab