From 48bf0252f08ea51113c02dad248b11b7a1eaf990 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org>
Date: Wed, 18 Jun 2014 11:44:18 +0100
Subject: [PATCH] Issue #2005434 by alexpott, heddn, andrewmacpherson, swentel:
 Let 3rd party modules store extra configuration in EntityDisplay.

---
 .../Core/Field/FieldDefinitionInterface.php   |  2 +
 core/lib/Drupal/Core/Field/FormatterBase.php  |  5 +-
 .../Core/Field/FormatterPluginManager.php     |  5 +-
 .../Drupal/Core/Field/PluginSettingsBase.php  | 24 +++++
 .../Core/Field/PluginSettingsInterface.php    | 31 +++++++
 core/lib/Drupal/Core/Field/WidgetBase.php     |  5 +-
 .../Drupal/Core/Field/WidgetPluginManager.php |  5 +-
 .../CommentDefaultFormatter.php               |  7 +-
 .../DateTimeDefaultFormatter.php              |  7 +-
 .../FieldWidget/DateTimeDefaultWidget.php     |  4 +-
 .../config/schema/entity.data_types.yml       | 16 +++-
 .../entity/config/schema/entity.schema.yml    | 18 +---
 core/modules/entity/src/EntityDisplayBase.php |  6 ++
 .../entity/src/Tests/EntityDisplayTest.php    |  4 +
 .../src/Tests/EntityFormDisplayTest.php       |  3 +
 .../config/schema/field_test.schema.yml       | 23 +++++
 .../modules/field_test/field_test.module      | 38 --------
 .../schema/field_third_party_test.schema.yml  | 15 ++++
 .../field_third_party_test.info.yml           |  9 ++
 .../field_third_party_test.module             | 42 +++++++++
 core/modules/field_ui/field_ui.api.php        | 89 +++++++++++--------
 core/modules/field_ui/src/DisplayOverview.php | 26 ++++--
 .../field_ui/src/DisplayOverviewBase.php      | 59 ++++++++----
 .../field_ui/src/FormDisplayOverview.php      | 25 ++++--
 .../field_ui/src/Tests/ManageDisplayTest.php  | 46 +++++++---
 ...m_display.taxonomy_term.forums.default.yml |  2 +
 ...w_display.taxonomy_term.forums.default.yml |  4 +
 ....migration.d6_field_formatter_settings.yml |  2 +
 ...tion.d6_field_instance_widget_settings.yml |  2 +
 .../d6/MigrateFieldFormatterSettingsTest.php  |  1 +
 .../d6/MigrateFieldWidgetSettingsTest.php     |  1 +
 .../Field/FieldWidget/OptionsWidgetBase.php   |  4 +-
 ...tity.form_display.node.article.default.yml |  5 ++
 .../entity.form_display.user.user.default.yml |  1 +
 ...tity.view_display.node.article.default.yml |  9 +-
 ...ntity.view_display.node.article.teaser.yml | 11 ++-
 .../entity.view_display.user.user.compact.yml |  5 +-
 .../entity.view_display.user.user.default.yml |  5 +-
 38 files changed, 401 insertions(+), 165 deletions(-)
 create mode 100644 core/modules/field/tests/modules/field_test/config/schema/field_test.schema.yml
 create mode 100644 core/modules/field/tests/modules/field_third_party_test/config/schema/field_third_party_test.schema.yml
 create mode 100644 core/modules/field/tests/modules/field_third_party_test/field_third_party_test.info.yml
 create mode 100644 core/modules/field/tests/modules/field_third_party_test/field_third_party_test.module

diff --git a/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php b/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php
index 20838192027b..8a7fb98a11de 100644
--- a/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php
+++ b/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php
@@ -91,6 +91,8 @@ public function isDisplayConfigurable($display_context);
    *     for the field type will be used.
    *   - settings: (array) Settings for the plugin specified above. The default
    *     settings for the plugin will be used for settings left unspecified.
+   *   - third_party_settings: (array) Settings provided by other extensions
+   *     through hook_field_formatter_third_party_settings_form().
    *   - weight: (float) The weight of the element. Not needed if 'type' is
    *     'hidden'.
    *   The defaults of the various display options above get applied by the used
diff --git a/core/lib/Drupal/Core/Field/FormatterBase.php b/core/lib/Drupal/Core/Field/FormatterBase.php
index eeb94a349e37..bc3cdb653e26 100644
--- a/core/lib/Drupal/Core/Field/FormatterBase.php
+++ b/core/lib/Drupal/Core/Field/FormatterBase.php
@@ -55,14 +55,17 @@ abstract class FormatterBase extends PluginSettingsBase implements FormatterInte
    *   The formatter label display setting.
    * @param string $view_mode
    *   The view mode.
+   * @param array $third_party_settings
+   *   Any third party settings settings.
    */
-  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode) {
+  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings) {
     parent::__construct(array(), $plugin_id, $plugin_definition);
 
     $this->fieldDefinition = $field_definition;
     $this->settings = $settings;
     $this->label = $label;
     $this->viewMode = $view_mode;
+    $this->thirdPartySettings = $third_party_settings;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Field/FormatterPluginManager.php b/core/lib/Drupal/Core/Field/FormatterPluginManager.php
index 0d9e516f66fd..c16d23ff5a69 100644
--- a/core/lib/Drupal/Core/Field/FormatterPluginManager.php
+++ b/core/lib/Drupal/Core/Field/FormatterPluginManager.php
@@ -68,7 +68,7 @@ public function createInstance($plugin_id, array $configuration = array()) {
       return $plugin_class::create(\Drupal::getContainer(), $configuration, $plugin_id, $plugin_definition);
     }
 
-    return new $plugin_class($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['label'], $configuration['view_mode']);
+    return new $plugin_class($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['label'], $configuration['view_mode'], $configuration['third_party_settings']);
   }
 
   /**
@@ -91,6 +91,8 @@ public function createInstance($plugin_id, array $configuration = array()) {
    *       also be used if the requested formatter is not available.
    *     - settings: (array) Settings specific to the formatter. Each setting
    *       defaults to the default value specified in the formatter definition.
+   *     - third_party_settings: (array) Settings provided by other extensions
+   *       through hook_field_formatter_third_party_settings_form().
    *
    * @return \Drupal\Core\Field\FormatterInterface|null
    *   A formatter object or NULL when plugin is not found.
@@ -143,6 +145,7 @@ public function prepareConfiguration($field_type, array $configuration) {
     $configuration += array(
       'label' => 'above',
       'settings' => array(),
+      'third_party_settings' => array(),
     );
     // If no formatter is specified, use the default formatter.
     if (!isset($configuration['type'])) {
diff --git a/core/lib/Drupal/Core/Field/PluginSettingsBase.php b/core/lib/Drupal/Core/Field/PluginSettingsBase.php
index fb8e07b558c1..37f69f3e4c6d 100644
--- a/core/lib/Drupal/Core/Field/PluginSettingsBase.php
+++ b/core/lib/Drupal/Core/Field/PluginSettingsBase.php
@@ -23,6 +23,15 @@ abstract class PluginSettingsBase extends PluginBase implements PluginSettingsIn
    */
   protected $settings = array();
 
+  /**
+   * The plugin settings injected by third party modules.
+   *
+   * @see hooks
+   *
+   * @var array
+   */
+  protected $thirdPartySettings = array();
+
   /**
    * Whether default settings have been merged into the current $settings.
    *
@@ -84,4 +93,19 @@ public function setSetting($key, $value) {
     return $this;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getThirdPartySetting($module, $key, $default = NULL) {
+    return isset($this->thirdPartySettings[$module][$key]) ? $this->thirdPartySettings[$module][$key] : $default;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setThirdPartySetting($module, $key, $value) {
+    $this->thirdPartySettings[$module][$key] = $value;
+    return $this;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Field/PluginSettingsInterface.php b/core/lib/Drupal/Core/Field/PluginSettingsInterface.php
index 9f9b7c6d41b1..cc64850a0794 100644
--- a/core/lib/Drupal/Core/Field/PluginSettingsInterface.php
+++ b/core/lib/Drupal/Core/Field/PluginSettingsInterface.php
@@ -64,4 +64,35 @@ public function setSettings(array $settings);
    */
   public function setSetting($key, $value);
 
+  /**
+   * Returns the value of a third-party setting, or $default if not set.
+   *
+   * @param string $module
+   *   The module providing the third-party setting.
+   * @param string $key
+   *   The setting name.
+   * @param mixed $default
+   *   (optional) The default value if the third party setting is not set.
+   *   Defaults to NULL.
+   *
+   * @return mixed|NULL
+   *   The setting value. Returns NULL if the setting does not exist and
+   *   $default is not provided.
+   */
+  public function getThirdPartySetting($module, $key, $default = NULL);
+
+  /**
+   * Sets the value of a third-party setting for the plugin.
+   *
+   * @param string $module
+   *   The module providing the third-party setting.
+   * @param string $key
+   *   The setting name.
+   * @param mixed $value
+   *   The setting value.
+   *
+   * @return $this
+   */
+  public function setThirdPartySetting($module, $key, $value);
+
 }
diff --git a/core/lib/Drupal/Core/Field/WidgetBase.php b/core/lib/Drupal/Core/Field/WidgetBase.php
index ef04793ffe7f..fa61dd2c6e20 100644
--- a/core/lib/Drupal/Core/Field/WidgetBase.php
+++ b/core/lib/Drupal/Core/Field/WidgetBase.php
@@ -43,11 +43,14 @@ abstract class WidgetBase extends PluginSettingsBase implements WidgetInterface
    *   The definition of the field to which the widget is associated.
    * @param array $settings
    *   The widget settings.
+   * @param array $third_party_settings
+   *   Any third party settings settings.
    */
-  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings) {
+  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings) {
     parent::__construct(array(), $plugin_id, $plugin_definition);
     $this->fieldDefinition = $field_definition;
     $this->settings = $settings;
+    $this->thirdPartySettings = $third_party_settings;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Field/WidgetPluginManager.php b/core/lib/Drupal/Core/Field/WidgetPluginManager.php
index 55c1fc5eee9d..7b58b6c148e4 100644
--- a/core/lib/Drupal/Core/Field/WidgetPluginManager.php
+++ b/core/lib/Drupal/Core/Field/WidgetPluginManager.php
@@ -71,6 +71,8 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac
    *       used if the requested widget is not available.
    *     - settings: (array) Settings specific to the widget. Each setting
    *       defaults to the default value specified in the widget definition.
+   *     - third_party_settings: (array) Settings provided by other extensions
+   *       through hook_field_formatter_third_party_settings_form().
    *
    * @return \Drupal\Core\Field\WidgetInterface|null
    *   A Widget object or NULL when plugin is not found.
@@ -124,7 +126,7 @@ public function createInstance($plugin_id, array $configuration = array()) {
       return $plugin_class::create(\Drupal::getContainer(), $configuration, $plugin_id, $plugin_definition);
     }
 
-    return new $plugin_class($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings']);
+    return new $plugin_class($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['third_party_settings']);
   }
 
 
@@ -143,6 +145,7 @@ public function prepareConfiguration($field_type, array $configuration) {
     // Fill in defaults for missing properties.
     $configuration += array(
       'settings' => array(),
+      'third_party_settings' => array(),
     );
     // If no widget is specified, use the default widget.
     if (!isset($configuration['type'])) {
diff --git a/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php b/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php
index bb17d42dad3d..77681c9ae4f2 100644
--- a/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php
+++ b/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php
@@ -75,6 +75,7 @@ public static function create(ContainerInterface $container, array $configuratio
       $configuration['settings'],
       $configuration['label'],
       $configuration['view_mode'],
+      $configuration['third_party_settings'],
       $container->get('current_user'),
       $container->get('entity.manager')->getStorage('comment'),
       $container->get('entity.manager')->getViewBuilder('comment')
@@ -96,6 +97,8 @@ public static function create(ContainerInterface $container, array $configuratio
    *   The formatter label display setting.
    * @param string $view_mode
    *   The view mode.
+   * @param array $third_party_settings
+   *   Third party settings.
    * @param \Drupal\Core\Session\AccountInterface $current_user
    *   The current user.
    * @param \Drupal\comment\CommentStorageInterface $comment_storage
@@ -103,8 +106,8 @@ public static function create(ContainerInterface $container, array $configuratio
    * @param \Drupal\Core\Entity\EntityViewBuilderInterface $comment_view_builder
    *   The comment view builder.
    */
-  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, AccountInterface $current_user, CommentStorageInterface $comment_storage, EntityViewBuilderInterface $comment_view_builder) {
-    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode);
+  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, AccountInterface $current_user, CommentStorageInterface $comment_storage, EntityViewBuilderInterface $comment_view_builder) {
+    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
     $this->viewBuilder = $comment_view_builder;
     $this->storage = $comment_storage;
     $this->currentUser = $current_user;
diff --git a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeDefaultFormatter.php b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeDefaultFormatter.php
index 0285f02fe5d2..239f454e5fa8 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeDefaultFormatter.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldFormatter/DateTimeDefaultFormatter.php
@@ -67,13 +67,15 @@ public static function defaultSettings() {
    *   The formatter label display setting.
    * @param string $view_mode
    *   The view mode.
+   * @param array $third_party_settings
+   *   Third party settings.
    * @param \Drupal\Core\Datetime\Date $date_service
    *   The date service.
    * @param \Drupal\Core\Entity\EntityStorageInterface $date_storage
    *   The date storage.
    */
-  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, Date $date_service, EntityStorageInterface $date_storage) {
-    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode);
+  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, Date $date_service, EntityStorageInterface $date_storage) {
+    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
 
     $this->dateService = $date_service;
     $this->dateStorage = $date_storage;
@@ -90,6 +92,7 @@ public static function create(ContainerInterface $container, array $configuratio
       $configuration['settings'],
       $configuration['label'],
       $configuration['view_mode'],
+      $configuration['third_party_settings'],
       $container->get('date'),
       $container->get('entity.manager')->getStorage('date_format')
     );
diff --git a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDefaultWidget.php b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDefaultWidget.php
index 161bada4d6de..9b2f3bdd673d 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDefaultWidget.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldWidget/DateTimeDefaultWidget.php
@@ -34,8 +34,8 @@ class DateTimeDefaultWidget extends WidgetBase {
   /**
    * {@inheritdoc}
    */
-  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings) {
-    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings);
+  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings) {
+    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
 
     // @todo Inject this once https://drupal.org/node/2035317 is in.
     $this->dateStorage = \Drupal::entityManager()->getStorage('date_format');
diff --git a/core/modules/entity/config/schema/entity.data_types.yml b/core/modules/entity/config/schema/entity.data_types.yml
index dcfd62c2712e..f0b87eece186 100644
--- a/core/modules/entity/config/schema/entity.data_types.yml
+++ b/core/modules/entity/config/schema/entity.data_types.yml
@@ -4,15 +4,20 @@
 entity_field_view_display_base:
   type: mapping
   mapping:
-    label:
-      type: string
-      label: 'Label setting machine name'
     type:
       type: string
       label: 'Format type machine name'
     weight:
       type: integer
       label: 'Weight'
+    third_party_settings:
+      type: sequence
+      label: 'Third party settings'
+      sequence:
+        - type: entity_view_display.third_party.[%key]
+    label:
+      type: string
+      label: 'Label setting machine name'
 
 # Schema for the base of the form mode display format settings.
 entity_field_form_display_base:
@@ -24,3 +29,8 @@ entity_field_form_display_base:
     weight:
       type: integer
       label: 'Weight'
+    third_party_settings:
+      type: sequence
+      label: 'Third party settings'
+      sequence:
+        - type: entity_form_display.third_party.[%key]
diff --git a/core/modules/entity/config/schema/entity.schema.yml b/core/modules/entity/config/schema/entity.schema.yml
index 074d82a60c12..a924badd6cc5 100644
--- a/core/modules/entity/config/schema/entity.schema.yml
+++ b/core/modules/entity/config/schema/entity.schema.yml
@@ -115,27 +115,13 @@ entity.form_display.*.*.*:
 
 # Default schema for entity display field with undefined type.
 entity_view_display.field.*:
-  type: mapping
+  type: entity_field_view_display_base
   label: 'Entity display default'
-  mapping:
-    visible:
-      type: boolean
-      label: 'Visibility'
-    weight:
-      type: integer
-      label: 'Weight'
 
 # Default schema for entity form display field with undefined type.
 entity_form_display.field.*:
-  type: mapping
+  type: entity_field_form_display_base
   label: 'Entity form display default'
-  mapping:
-    visible:
-      type: boolean
-      label: 'Visibility'
-    weight:
-      type: integer
-      label: 'Weight'
 
 entity_form_display.field.string:
   type: entity_field_form_display_base
diff --git a/core/modules/entity/src/EntityDisplayBase.php b/core/modules/entity/src/EntityDisplayBase.php
index a9b99d26534e..9a1208b5d4c3 100644
--- a/core/modules/entity/src/EntityDisplayBase.php
+++ b/core/modules/entity/src/EntityDisplayBase.php
@@ -177,6 +177,12 @@ public function calculateDependencies() {
       if (isset($component['type']) && $definition = $this->pluginManager->getDefinition($component['type'], FALSE)) {
         $this->addDependency('module', $definition['provider']);
       }
+      // Create dependencies on any modules providing third party settings.
+      if (isset($component['third_party_settings'])) {
+        foreach($component['third_party_settings'] as $module => $settings) {
+          $this->addDependency('module', $module);
+        }
+      }
     }
     // Depend on configured modes.
     if ($this->mode != 'default') {
diff --git a/core/modules/entity/src/Tests/EntityDisplayTest.php b/core/modules/entity/src/Tests/EntityDisplayTest.php
index 26e47d67b9ed..f0c8e0c33dd4 100644
--- a/core/modules/entity/src/Tests/EntityDisplayTest.php
+++ b/core/modules/entity/src/Tests/EntityDisplayTest.php
@@ -69,6 +69,7 @@ public function testEntityDisplayCRUD() {
       'type' => 'string',
       'weight' => -5,
       'settings' => array(),
+      'third_party_settings' => array(),
     );
     $this->assertEqual($display->getComponents(), $expected);
 
@@ -174,6 +175,7 @@ public function testFieldComponent() {
       'label' => 'above',
       'type' => $default_formatter,
       'settings' => $formatter_settings,
+      'third_party_settings' => array(),
     );
     $this->assertEqual($display->getComponent($field_name), $expected);
 
@@ -221,12 +223,14 @@ public function testBaseFieldComponent() {
         'label' => 'above',
         'type' => 'text_default',
         'settings' => $formatter_settings,
+        'third_party_settings' => array(),
         'weight' => 10,
       ),
       'test_display_non_configurable' => array(
         'label' => 'above',
         'type' => 'text_default',
         'settings' => $formatter_settings,
+        'third_party_settings' => array(),
         'weight' => 11,
       ),
     );
diff --git a/core/modules/entity/src/Tests/EntityFormDisplayTest.php b/core/modules/entity/src/Tests/EntityFormDisplayTest.php
index aa47ca64ae93..6caf7758b020 100644
--- a/core/modules/entity/src/Tests/EntityFormDisplayTest.php
+++ b/core/modules/entity/src/Tests/EntityFormDisplayTest.php
@@ -84,6 +84,7 @@ public function testFieldComponent() {
       'weight' => 0,
       'type' => $default_widget,
       'settings' => $widget_settings,
+      'third_party_settings' => array(),
     );
     $this->assertEqual($form_display->getComponent($field_name), $expected);
 
@@ -136,11 +137,13 @@ public function testBaseFieldComponent() {
       'test_display_configurable' => array(
         'type' => 'text_textfield',
         'settings' => $formatter_settings,
+        'third_party_settings' => array(),
         'weight' => 10,
       ),
       'test_display_non_configurable' => array(
         'type' => 'text_textfield',
         'settings' => $formatter_settings,
+        'third_party_settings' => array(),
         'weight' => 11,
       ),
     );
diff --git a/core/modules/field/tests/modules/field_test/config/schema/field_test.schema.yml b/core/modules/field/tests/modules/field_test/config/schema/field_test.schema.yml
new file mode 100644
index 000000000000..f9ef7b7198d4
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/config/schema/field_test.schema.yml
@@ -0,0 +1,23 @@
+entity_view_display.field.field_test_multiple:
+  type: entity_field_view_display_base
+  label: 'field_test display format settings'
+  mapping:
+    settings:
+      type: mapping
+      label: 'Settings'
+      mapping:
+        test_formatter_setting_multiple:
+          type: string
+          label: 'Test setting'
+
+entity_form_display.field.test_field_widget_multiple:
+  type: entity_field_form_display_base
+  label: 'field_test display format settings'
+  mapping:
+    settings:
+      type: mapping
+      label: 'Settings'
+      mapping:
+        test_widget_setting_multiple:
+          type: string
+          label: 'Test setting'
diff --git a/core/modules/field/tests/modules/field_test/field_test.module b/core/modules/field/tests/modules/field_test/field_test.module
index 45a0c0dad046..6c9b7d4b2e6e 100644
--- a/core/modules/field/tests/modules/field_test/field_test.module
+++ b/core/modules/field/tests/modules/field_test/field_test.module
@@ -155,44 +155,6 @@ function field_test_query_efq_metadata_test_alter(&$query) {
   $efq_test_metadata = $query->getMetadata('foo');
 }
 
-/**
- * Implements hook_field_widget_settings_form_alter().
- */
-function field_test_field_widget_settings_form_alter(&$element, &$form_state, $context) {
-  $element['field_test_widget_settings_form_alter'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Widget settings form alter'),
-    '#default_value' => $context['widget']->getSetting('field_test_widget_settings_form_alter'),
-  );
-}
-
-/**
- * Implements hook_field_widget_settings_summary_alter().
- */
-function field_test_field_widget_settings_summary_alter(&$summary, $context) {
-  $summary[] = 'field_test_field_widget_settings_summary_alter';
-  return $summary;
-}
-
-/**
- * Implements hook_field_formatter_settings_form_alter().
- */
-function field_test_field_formatter_settings_form_alter(&$element, &$form_state, $context) {
-  $element['field_test_formatter_settings_form_alter'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Formatter settings form alter'),
-    '#default_value' => $context['formatter']->getSetting('field_test_formatter_settings_form_alter'),
-  );
-}
-
-/**
- * Implements hook_field_formatter_settings_summary_alter().
- */
-function field_test_field_formatter_settings_summary_alter(&$summary, $context) {
-  $summary[] = 'field_test_field_formatter_settings_summary_alter';
-  return $summary;
-}
-
 /**
  * Implements hook_entity_extra_field_info_alter().
  */
diff --git a/core/modules/field/tests/modules/field_third_party_test/config/schema/field_third_party_test.schema.yml b/core/modules/field/tests/modules/field_third_party_test/config/schema/field_third_party_test.schema.yml
new file mode 100644
index 000000000000..c177f4404ef1
--- /dev/null
+++ b/core/modules/field/tests/modules/field_third_party_test/config/schema/field_third_party_test.schema.yml
@@ -0,0 +1,15 @@
+entity_view_display.third_party.field_third_party_test:
+  type: mapping
+  label: 'field_third_party_test entity display settings'
+  mapping:
+    field_test_field_formatter_third_party_settings_form:
+      type: string
+      label: field_test_field_formatter_third_party_settings_form
+
+entity_form_display.third_party.field_third_party_test:
+  type: mapping
+  label: 'field_third_party_test entity form display settings'
+  mapping:
+    field_test_widget_third_party_settings_form:
+      type: string
+      label: field_test_widget_third_party_settings_form
diff --git a/core/modules/field/tests/modules/field_third_party_test/field_third_party_test.info.yml b/core/modules/field/tests/modules/field_third_party_test/field_third_party_test.info.yml
new file mode 100644
index 000000000000..6fd7f39efb94
--- /dev/null
+++ b/core/modules/field/tests/modules/field_third_party_test/field_third_party_test.info.yml
@@ -0,0 +1,9 @@
+name: 'Field Third Party Settings Test'
+type: module
+description: 'Support module for the Field API tests.'
+core: 8.x
+package: Testing
+version: VERSION
+dependencies:
+  - entity_test
+  - field_test
diff --git a/core/modules/field/tests/modules/field_third_party_test/field_third_party_test.module b/core/modules/field/tests/modules/field_third_party_test/field_third_party_test.module
new file mode 100644
index 000000000000..5ac0d1c82457
--- /dev/null
+++ b/core/modules/field/tests/modules/field_third_party_test/field_third_party_test.module
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * Implements hook_field_widget_third_party_settings_form().
+ */
+function field_third_party_test_field_widget_third_party_settings_form(\Drupal\Core\Field\WidgetInterface $plugin, \Drupal\Core\Field\FieldDefinitionInterface $field_definition, $form_mode, $form, $form_state) {
+  $element['field_test_widget_third_party_settings_form'] = array(
+    '#type' => 'textfield',
+    '#title' => t('3rd party widget settings form'),
+    '#default_value' => $plugin->getThirdPartySetting('field_third_party_test', 'field_test_widget_third_party_settings_form'),
+  );
+  return $element;
+}
+
+/**
+ * Implements hook_field_widget_settings_summary_alter().
+ */
+function field_third_party_test_field_widget_settings_summary_alter(&$summary, $context) {
+  $summary[] = 'field_test_field_widget_settings_summary_alter';
+  return $summary;
+}
+
+/**
+ * Implements hook_field_formatter_third_party_settings_form().
+ */
+function field_third_party_test_field_formatter_third_party_settings_form(\Drupal\Core\Field\FormatterInterface $plugin, \Drupal\Core\Field\FieldDefinitionInterface $field_definition, $view_mode, $form, $form_state) {
+  $element['field_test_field_formatter_third_party_settings_form'] = array(
+    '#type' => 'textfield',
+    '#title' => t('3rd party formatter settings form'),
+    '#default_value' => $plugin->getThirdPartySetting('field_third_party_test', 'field_test_field_formatter_third_party_settings_form'),
+  );
+  return $element;
+}
+
+/**
+ * Implements hook_field_formatter_settings_summary_alter().
+ */
+function field_third_party_test_field_formatter_settings_summary_alter(&$summary, $context) {
+  $summary[] = 'field_test_field_formatter_settings_summary_alter';
+  return $summary;
+}
+
diff --git a/core/modules/field_ui/field_ui.api.php b/core/modules/field_ui/field_ui.api.php
index 255ce856bfa6..070f020d911f 100644
--- a/core/modules/field_ui/field_ui.api.php
+++ b/core/modules/field_ui/field_ui.api.php
@@ -11,65 +11,76 @@
  */
 
 /**
- * Alters the formatter settings form.
+ * Allow modules to add settings to field formatters provided by other modules.
  *
- * @param $element
- *   Form array.
- * @param $form_state
- *   The form state of the (entire) configuration form.
- * @param $context
- *   An associative array with the following elements:
- *   - formatter: The formatter object.
- *   - field_definition: The field definition.
- *   - view_mode: The view mode being configured.
- *   - form: The (entire) configuration form array.
+ * @param \Drupal\Core\Field\FormatterInterface $plugin
+ *   The instantiated field formatter plugin.
+ * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+ *   The field definition.
+ * @param $view_mode
+ *   The entity view mode.
+ * @param array $form
+ *   The (entire) configuration form array.
+ * @param array $form_state
+ *   The form state.
+ *
+ * @return array
+ *   Returns the form array to be built.
  *
  * @see \Drupal\field_ui\DisplayOverView.
  */
-function hook_field_formatter_settings_form_alter(&$element, &$form_state, $context) {
-  // Add a 'mysetting' checkbox to the settings form for 'foo_formatter'
-  // field formatters.
-  if ($context['formatter']->getPluginId() == 'foo_formatter') {
-    $element['mysetting'] = array(
+function hook_field_formatter_third_party_settings_form(\Drupal\Core\Field\FormatterInterface $plugin, \Drupal\Core\Field\FieldDefinitionInterface $field_definition, $view_mode, $form, $form_state) {
+  $element = array();
+  // Add a 'my_setting' checkbox to the settings form for 'foo_formatter' field
+  // formatters.
+  if ($plugin->getPluginId() == 'foo_formatter') {
+    $element['my_setting'] = array(
       '#type' => 'checkbox',
       '#title' => t('My setting'),
-      '#default_value' => $context['formatter']->getSetting('mysetting'),
+      '#default_value' => $plugin->getThirdPartySetting('my_module', 'my_setting'),
     );
   }
+  return $element;
 }
 
 /**
- * Alters the widget settings form.
+ * Allow modules to add settings to field widgets provided by other modules.
  *
- * @param array $element
- *   Form array.
+ * @param \Drupal\Core\Field\WidgetInterface $plugin
+ *   The instantiated field widget plugin.
+ * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+ *   The field definition.
+ * @param $form_mode
+ *   The entity form mode.
+ * @param array $form
+ *   The (entire) configuration form array.
  * @param array $form_state
- *   The form state of the (entire) configuration form.
- * @param array $context
- *   An associative array with the following elements:
- *   - formatter: The formatter object.
- *   - field_definition: The field definition.
- *   - form_mode: The form mode being configured.
- *   - form: The (entire) configuration form array.
+ *   The form state.
+ *
+ * @return array
+ *   Returns the form array to be built.
  *
  * @see \Drupal\field_ui\FormDisplayOverView.
  */
-function hook_field_widget_settings_form_alter(&$element, &$form_state, $context) {
-  // Add a 'mysetting' checkbox to the settings form for 'foo_field' fields.
-  if ($context['field']['type'] == 'foo_field') {
-    $element['mysetting'] = array(
+function hook_field_widget_third_party_settings_form(\Drupal\Core\Field\WidgetInterface $plugin, \Drupal\Core\Field\FieldDefinitionInterface $field_definition, $form_mode, $form, $form_state) {
+  $element = array();
+  // Add a 'my_setting' checkbox to the settings form for 'foo_widget' field
+  // widgets.
+  if ($plugin->getPluginId() == 'foo_widget') {
+    $element['my_setting'] = array(
       '#type' => 'checkbox',
       '#title' => t('My setting'),
-      '#default_value' => $context['formatter']->getSetting('mysetting'),
+      '#default_value' => $plugin->getThirdPartySetting('my_module', 'my_setting'),
     );
   }
+  return $element;
 }
 
 /**
  * Alters the field formatter settings summary.
  *
- * @param $summary
- *   The summary.
+ * @param array $summary
+ *   An array of summary messages.
  * @param $context
  *   An associative array with the following elements:
  *   - formatter: The formatter object.
@@ -82,7 +93,7 @@ function hook_field_formatter_settings_summary_alter(&$summary, $context) {
   // Append a message to the summary when an instance of foo_formatter has
   // mysetting set to TRUE for the current view mode.
   if ($context['formatter']->getPluginId() == 'foo_formatter') {
-    if ($context['formatter']->getSetting('mysetting')) {
+    if ($context['formatter']->getThirdPartySetting('my_module', 'my_setting')) {
       $summary[] = t('My setting enabled.');
     }
   }
@@ -92,7 +103,7 @@ function hook_field_formatter_settings_summary_alter(&$summary, $context) {
  * Alters the field widget settings summary.
  *
  * @param array $summary
- *   The summary.
+ *   An array of summary messages.
  * @param array $context
  *   An associative array with the following elements:
  *   - widget: The widget object.
@@ -102,10 +113,10 @@ function hook_field_formatter_settings_summary_alter(&$summary, $context) {
  * @see \Drupal\field_ui\FormDisplayOverView.
  */
 function hook_field_widget_settings_summary_alter(&$summary, $context) {
-  // Append a message to the summary when an instance of foo_field has
+  // Append a message to the summary when an instance of foo_widget has
   // mysetting set to TRUE for the current view mode.
-  if ($context['field']['type'] == 'foo_field') {
-    if ($context['widget']->getSetting('mysetting')) {
+  if ($context['widget']->getPluginId() == 'foo_widget') {
+    if ($context['widget']->getThirdPartySetting('my_module', 'my_setting')) {
       $summary[] = t('My setting enabled.');
     }
   }
diff --git a/core/modules/field_ui/src/DisplayOverview.php b/core/modules/field_ui/src/DisplayOverview.php
index 956d15fb8b08..f93ae84df522 100644
--- a/core/modules/field_ui/src/DisplayOverview.php
+++ b/core/modules/field_ui/src/DisplayOverview.php
@@ -14,6 +14,8 @@
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Field\FieldTypePluginManager;
+use Drupal\Core\Field\FormatterInterface;
+use Drupal\Core\Field\PluginSettingsInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -230,20 +232,26 @@ protected function getFieldLabelOptions() {
   /**
    * {@inheritdoc}
    */
-  protected function alterSettingsForm(array &$settings_form, $plugin, FieldDefinitionInterface $field_definition, array $form, array &$form_state) {
-    $context = array(
-      'formatter' => $plugin,
-      'field_definition' => $field_definition,
-      'view_mode' => $this->mode,
-      'form' => $form,
-    );
-    $this->moduleHandler->alter('field_formatter_settings_form', $settings_form, $form_state, $context);
+  protected function thirdPartySettingsForm(PluginSettingsInterface $plugin, FieldDefinitionInterface $field_definition, array $form, array &$form_state) {
+    $settings_form = array();
+    // Invoke hook_field_formatter_third_party_settings_form(), keying resulting
+    // subforms by module name.
+    foreach ($this->moduleHandler->getImplementations('field_formatter_third_party_settings_form') as $module) {
+      $settings_form[$module] = $this->moduleHandler->invoke($module, 'field_formatter_third_party_settings_form', array(
+        $plugin,
+        $field_definition,
+        $this->mode,
+        $form,
+        $form_state,
+      ));
+    }
+    return $settings_form;
   }
 
   /**
    * {@inheritdoc}
    */
-  protected function alterSettingsSummary(array &$summary, $plugin, FieldDefinitionInterface $field_definition) {
+  protected function alterSettingsSummary(array &$summary, PluginSettingsInterface $plugin, FieldDefinitionInterface $field_definition) {
     $context = array(
       'formatter' => $plugin,
       'field_definition' => $field_definition,
diff --git a/core/modules/field_ui/src/DisplayOverviewBase.php b/core/modules/field_ui/src/DisplayOverviewBase.php
index 5fc7240fd2ee..8a98e93e101d 100644
--- a/core/modules/field_ui/src/DisplayOverviewBase.php
+++ b/core/modules/field_ui/src/DisplayOverviewBase.php
@@ -14,6 +14,7 @@
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Field\PluginSettingsInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -323,8 +324,11 @@ protected function buildFieldRow(FieldDefinitionInterface $field_definition, Ent
     if (isset($form_state['values']['fields'][$field_name]['type'])) {
       $display_options['type'] = $form_state['values']['fields'][$field_name]['type'];
     }
-    if (isset($form_state['plugin_settings'][$field_name])) {
-      $display_options['settings'] = $form_state['plugin_settings'][$field_name];
+    if (isset($form_state['plugin_settings'][$field_name]['settings'])) {
+      $display_options['settings'] = $form_state['plugin_settings'][$field_name]['settings'];
+    }
+    if (isset($form_state['plugin_settings'][$field_name]['third_party_settings'])) {
+      $display_options['third_party_settings'] = $form_state['plugin_settings'][$field_name]['third_party_settings'];
     }
 
     // Get the corresponding plugin object.
@@ -349,9 +353,9 @@ protected function buildFieldRow(FieldDefinitionInterface $field_definition, Ent
       if ($plugin) {
         // Generate the settings form and allow other modules to alter it.
         $settings_form = $plugin->settingsForm($form, $form_state);
-        $this->alterSettingsForm($settings_form, $plugin, $field_definition, $form, $form_state);
+        $third_party_settings_form = $this->thirdPartySettingsForm($plugin, $field_definition, $form, $form_state);
 
-        if ($settings_form) {
+        if ($settings_form || $third_party_settings_form) {
           $field_row['plugin']['#cell_attributes'] = array('colspan' => 3);
           $field_row['plugin']['settings_edit_form'] = array(
             '#type' => 'container',
@@ -361,6 +365,7 @@ protected function buildFieldRow(FieldDefinitionInterface $field_definition, Ent
               '#markup' => $this->t('Plugin settings'),
             ),
             'settings' => $settings_form,
+            'third_party_settings' => $third_party_settings_form,
             'actions' => array(
               '#type' => 'actions',
               'save_settings' => $base_button + array(
@@ -405,7 +410,9 @@ protected function buildFieldRow(FieldDefinitionInterface $field_definition, Ent
         }
 
         // Check selected plugin settings to display edit link or not.
-        if ($this->pluginManager->getDefaultSettings($display_options['type'])) {
+        $settings_form = $plugin->settingsForm($form, $form_state);
+        $third_party_settings_form = $this->thirdPartySettingsForm($plugin, $field_definition, $form, $form_state);
+        if (!empty($settings_form) || !empty($third_party_settings_form)) {
           $field_row['settings_edit'] = $base_button + array(
             '#type' => 'image_button',
             '#name' => $field_name . '_settings_edit',
@@ -516,12 +523,22 @@ public function submitForm(array &$form, array &$form_state) {
         if (isset($values['settings_edit_form']['settings'])) {
           $settings = $values['settings_edit_form']['settings'];
         }
-        elseif (isset($form_state['plugin_settings'][$field_name])) {
-          $settings = $form_state['plugin_settings'][$field_name];
+        elseif (isset($form_state['plugin_settings'][$field_name]['settings'])) {
+          $settings = $form_state['plugin_settings'][$field_name]['settings'];
         }
         elseif ($current_options = $display->getComponent($field_name)) {
           $settings = $current_options['settings'];
         }
+        $third_party_settings = array();
+        if (isset($values['settings_edit_form']['third_party_settings'])) {
+          $third_party_settings = $values['settings_edit_form']['third_party_settings'];
+        }
+        elseif (isset($form_state['plugin_settings'][$field_name]['third_party_settings'])) {
+          $third_party_settings = $form_state['plugin_settings'][$field_name]['third_party_settings'];
+        }
+        elseif (($current_options = $display->getComponent($field_name)) && isset($current_options['third_party_settings'])) {
+          $third_party_settings = $current_options['third_party_settings'];
+        }
 
         // Only save settings actually used by the selected plugin.
         $default_settings = $this->pluginManager->getDefaultSettings($values['type']);
@@ -531,7 +548,8 @@ public function submitForm(array &$form, array &$form_state) {
         $component_values = array(
           'type' => $values['type'],
           'weight' => $values['weight'],
-          'settings' => $settings
+          'settings' => $settings,
+          'third_party_settings' => $third_party_settings,
         );
 
         // Only formatters have configurable label visibility.
@@ -604,8 +622,12 @@ public function multistepSubmit($form, &$form_state) {
       case 'update':
         // Store the saved settings, and set the field back to 'non edit' mode.
         $field_name = $trigger['#field_name'];
-        $values = $form_state['values']['fields'][$field_name]['settings_edit_form']['settings'];
-        $form_state['plugin_settings'][$field_name] = $values;
+        if (isset($form_state['values']['fields'][$field_name]['settings_edit_form']['settings'])) {
+          $form_state['plugin_settings'][$field_name]['settings'] = $form_state['values']['fields'][$field_name]['settings_edit_form']['settings'];
+        }
+        if (isset($form_state['values']['fields'][$field_name]['settings_edit_form']['third_party_settings'])) {
+          $form_state['plugin_settings'][$field_name]['third_party_settings'] = $form_state['values']['fields'][$field_name]['settings_edit_form']['third_party_settings'];
+        }
         unset($form_state['plugin_settings_edit']);
         break;
 
@@ -850,31 +872,32 @@ abstract protected function getTableHeader();
   abstract protected function getOverviewRoute($mode);
 
   /**
-   * Alters the widget or formatter settings form.
+   * Adds the widget or formatter third party settings forms.
    *
-   * @param array $settings_form
-   *   The widget or formatter settings form.
-   * @param object $plugin
+   * @param \Drupal\Core\Field\PluginSettingsInterface $plugin
    *   The widget or formatter.
    * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
    *   The field definition.
    * @param array $form
-   *   The The (entire) configuration form array.
+   *   The (entire) configuration form array.
    * @param array $form_state
    *   The form state.
+   *
+   * @return array
+   *   The widget or formatter third party settings form.
    */
-  abstract protected function alterSettingsForm(array &$settings_form, $plugin, FieldDefinitionInterface $field_definition, array $form, array &$form_state);
+  abstract protected function thirdPartySettingsForm(PluginSettingsInterface $plugin, FieldDefinitionInterface $field_definition, array $form, array &$form_state);
 
   /**
    * Alters the widget or formatter settings summary.
    *
    * @param array $summary
    *   The widget or formatter settings summary.
-   * @param object $plugin
+   * @param \Drupal\Core\Field\PluginSettingsInterface $plugin
    *   The widget or formatter.
    * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
    *   The field definition.
    */
-  abstract protected function alterSettingsSummary(array &$summary, $plugin, FieldDefinitionInterface $field_definition);
+  abstract protected function alterSettingsSummary(array &$summary, PluginSettingsInterface $plugin, FieldDefinitionInterface $field_definition);
 
 }
diff --git a/core/modules/field_ui/src/FormDisplayOverview.php b/core/modules/field_ui/src/FormDisplayOverview.php
index 7c0a5ff06633..3a2546ba102a 100644
--- a/core/modules/field_ui/src/FormDisplayOverview.php
+++ b/core/modules/field_ui/src/FormDisplayOverview.php
@@ -14,6 +14,7 @@
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldTypePluginManager;
+use Drupal\Core\Field\PluginSettingsInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -182,20 +183,26 @@ protected function getOverviewRoute($mode) {
   /**
    * {@inheritdoc}
    */
-  protected function alterSettingsForm(array &$settings_form, $plugin, FieldDefinitionInterface $field_definition, array $form, array &$form_state) {
-    $context = array(
-      'widget' => $plugin,
-      'field_definition' => $field_definition,
-      'form_mode' => $this->mode,
-      'form' => $form,
-    );
-    $this->moduleHandler->alter('field_widget_settings_form', $settings_form, $form_state, $context);
+  protected function thirdPartySettingsForm(PluginSettingsInterface $plugin, FieldDefinitionInterface $field_definition, array $form, array &$form_state) {
+    $settings_form = array();
+    // Invoke hook_field_widget_third_party_settings_form(), keying resulting
+    // subforms by module name.
+    foreach ($this->moduleHandler->getImplementations('field_widget_third_party_settings_form') as $module) {
+      $settings_form[$module] = $this->moduleHandler->invoke($module, 'field_widget_third_party_settings_form', array(
+        $plugin,
+        $field_definition,
+        $this->mode,
+        $form,
+        $form_state,
+      ));
+    }
+    return $settings_form;
   }
 
   /**
    * {@inheritdoc}
    */
-  protected function alterSettingsSummary(array &$summary, $plugin, FieldDefinitionInterface $field_definition) {
+  protected function alterSettingsSummary(array &$summary, PluginSettingsInterface $plugin, FieldDefinitionInterface $field_definition) {
     $context = array(
       'widget' => $plugin,
       'field_definition' => $field_definition,
diff --git a/core/modules/field_ui/src/Tests/ManageDisplayTest.php b/core/modules/field_ui/src/Tests/ManageDisplayTest.php
index c98d52dadc65..ef3ceb366d54 100644
--- a/core/modules/field_ui/src/Tests/ManageDisplayTest.php
+++ b/core/modules/field_ui/src/Tests/ManageDisplayTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\field_ui\Tests;
 
+use Drupal\config\Tests\SchemaCheckTestTrait;
 use Drupal\Core\Entity\EntityInterface;
 
 /**
@@ -14,12 +15,14 @@
  */
 class ManageDisplayTest extends FieldUiTestBase {
 
+  use SchemaCheckTestTrait;
+
   /**
    * Modules to enable.
    *
    * @var array
    */
-  public static $modules = array('search', 'field_test');
+  public static $modules = array('search', 'field_test', 'field_third_party_test');
 
   public static function getInfo() {
     return array(
@@ -100,13 +103,22 @@ function testFormatterUI() {
     $this->drupalPostAjaxForm(NULL, array(), "field_test_settings_edit");
 
     // Assert that the field added in
-    // field_test_field_formatter_settings_form_alter() is present.
-    $fieldname = 'fields[field_test][settings_edit_form][settings][field_test_formatter_settings_form_alter]';
-    $this->assertField($fieldname, 'The field added in hook_field_formatter_settings_form_alter() is present on the settings form.');
+    // field_test_field_formatter_third_party_settings_form() is present.
+    $fieldname = 'fields[field_test][settings_edit_form][third_party_settings][field_third_party_test][field_test_field_formatter_third_party_settings_form]';
+    $this->assertField($fieldname, 'The field added in hook_field_formatter_third_party_settings_form() is present on the settings form.');
     $edit = array($fieldname => 'foo');
     $this->drupalPostAjaxForm(NULL, $edit, "field_test_plugin_settings_update");
 
-    // Confirm that the extra settings are not updated on the settings form.
+    // Save the form to save the third party settings.
+    $this->drupalPostForm(NULL, array(), t('Save'));
+
+    \Drupal::entityManager()->clearCachedFieldDefinitions();
+    $display = entity_load('entity_view_display', 'node.' . $this->type . '.default', TRUE);
+    $this->assertEqual($display->getRenderer('field_test')->getThirdPartySetting('field_third_party_test', 'field_test_field_formatter_third_party_settings_form'), 'foo');
+    $this->assertTrue(in_array('field_third_party_test', $display->calculateDependencies()['module']), 'The display has a dependency on field_third_party_test module.');
+    $this->assertConfigSchema(\Drupal::service('config.typed'), $display->getEntityType()->getConfigPrefix() . '.' . $display->id(), $display->toArray());
+
+    // Confirm that the third party settings are not updated on the settings form.
     $this->drupalPostAjaxForm(NULL, array(), "field_test_settings_edit");
     $this->assertFieldByName($fieldname, '');
 
@@ -121,9 +133,15 @@ function testFormatterUI() {
     $this->drupalPostAjaxForm(NULL, $edit, "field_test_plugin_settings_update");
     $this->assertText('Default empty setting now has a value.');
 
-    // Test the no settings form behavior.
+    // Test the settings form behavior. An edit button should be present since
+    // there are third party settings to configure.
     $edit = array('fields[field_test][type]' => 'field_no_settings', 'refresh_rows' => 'field_test');
     $this->drupalPostAjaxForm(NULL, $edit, array('op' => t('Refresh')));
+    $this->assertFieldByName('field_test_settings_edit');
+    // Uninstall the module providing third party settings and ensure the button
+    // is no longer there.
+    \Drupal::moduleHandler()->uninstall(array('field_third_party_test'));
+    $this->drupalGet($manage_display);
     $this->assertNoFieldByName('field_test_settings_edit');
   }
 
@@ -193,13 +211,21 @@ public function testWidgetUI() {
     $this->drupalPostAjaxForm(NULL, array(), "field_test_settings_edit");
 
     // Assert that the field added in
-    // field_test_field_widget_settings_form_alter() is present.
-    $fieldname = 'fields[field_test][settings_edit_form][settings][field_test_widget_settings_form_alter]';
-    $this->assertField($fieldname, 'The field added in hook_field_widget_settings_form_alter() is present on the settings form.');
+    // field_test_field_widget_third_party_settings_form() is present.
+    $fieldname = 'fields[field_test][settings_edit_form][third_party_settings][field_third_party_test][field_test_widget_third_party_settings_form]';
+    $this->assertField($fieldname, 'The field added in hook_field_widget_third_party_settings_form() is present on the settings form.');
     $edit = array($fieldname => 'foo');
     $this->drupalPostAjaxForm(NULL, $edit, "field_test_plugin_settings_update");
 
-    // Confirm that the extra settings are not updated on the settings form.
+    // Save the form to save the third party settings.
+    $this->drupalPostForm(NULL, array(), t('Save'));
+    \Drupal::entityManager()->clearCachedFieldDefinitions();
+    $display = entity_load('entity_form_display', 'node.' . $this->type . '.default', TRUE);
+    $this->assertEqual($display->getRenderer('field_test')->getThirdPartySetting('field_third_party_test', 'field_test_widget_third_party_settings_form'), 'foo');
+    $this->assertTrue(in_array('field_third_party_test', $display->calculateDependencies()['module']), 'Form display does not have a dependency on field_third_party_test module.');
+    $this->assertConfigSchema(\Drupal::service('config.typed'), $display->getEntityType()->getConfigPrefix() . '.' . $display->id(), $display->toArray());
+
+    // Confirm that the third party settings are not updated on the settings form.
     $this->drupalPostAjaxForm(NULL, array(), "field_test_settings_edit");
     $this->assertFieldByName($fieldname, '');
   }
diff --git a/core/modules/forum/config/install/entity.form_display.taxonomy_term.forums.default.yml b/core/modules/forum/config/install/entity.form_display.taxonomy_term.forums.default.yml
index 9721a0afa868..d295232be240 100644
--- a/core/modules/forum/config/install/entity.form_display.taxonomy_term.forums.default.yml
+++ b/core/modules/forum/config/install/entity.form_display.taxonomy_term.forums.default.yml
@@ -10,10 +10,12 @@ content:
     settings:
       size: 60
       placeholder: ''
+    third_party_settings: {  }
   description:
     type: text_textfield
     weight: 0
     settings: { }
+    third_party_settings: {  }
 dependencies:
   entity:
     - taxonomy.vocabulary.forums
diff --git a/core/modules/forum/config/install/entity.view_display.taxonomy_term.forums.default.yml b/core/modules/forum/config/install/entity.view_display.taxonomy_term.forums.default.yml
index cefa7535ba18..f2f5b610f2c3 100644
--- a/core/modules/forum/config/install/entity.view_display.taxonomy_term.forums.default.yml
+++ b/core/modules/forum/config/install/entity.view_display.taxonomy_term.forums.default.yml
@@ -5,7 +5,11 @@ mode: default
 status: true
 content:
   description:
+    type: text_default
     weight: 0
+    settings: {  }
+    third_party_settings: {  }
+    label: above
 hidden: {  }
 status: true
 dependencies:
diff --git a/core/modules/migrate_drupal/config/install/migrate.migration.d6_field_formatter_settings.yml b/core/modules/migrate_drupal/config/install/migrate.migration.d6_field_formatter_settings.yml
index 582c4705c87b..2d76591bad30 100644
--- a/core/modules/migrate_drupal/config/install/migrate.migration.d6_field_formatter_settings.yml
+++ b/core/modules/migrate_drupal/config/install/migrate.migration.d6_field_formatter_settings.yml
@@ -4,6 +4,7 @@ source:
   plugin: d6_field_instance_per_view_mode
   constants:
     entity_type: node
+    third_party_settings: { }
 
 process:
   # We skip field types that don't exist because they weren't migrated by the
@@ -290,6 +291,7 @@ process:
             trim_length: 600
     -
       plugin: field_formatter_settings_defaults
+  "options.third_party_settings": constants.third_party_settings
 
 destination:
   plugin: component_entity_display
diff --git a/core/modules/migrate_drupal/config/install/migrate.migration.d6_field_instance_widget_settings.yml b/core/modules/migrate_drupal/config/install/migrate.migration.d6_field_instance_widget_settings.yml
index 61ed23b68788..9f169e1e15b3 100644
--- a/core/modules/migrate_drupal/config/install/migrate.migration.d6_field_instance_widget_settings.yml
+++ b/core/modules/migrate_drupal/config/install/migrate.migration.d6_field_instance_widget_settings.yml
@@ -5,6 +5,7 @@ source:
   constants:
     entity_type: node
     form_mode: default
+    third_party_settings: { }
 
 process:
   # We skip field types that don't exist because they weren't migrated by the
@@ -50,6 +51,7 @@ process:
       source:
         - widget_type
         - widget_settings
+  "options.third_party_settings": constants.third_party_settings
 
 destination:
   plugin: component_entity_form_display
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldFormatterSettingsTest.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldFormatterSettingsTest.php
index dd85245fe97c..fdea39e6dba5 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldFormatterSettingsTest.php
+++ b/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldFormatterSettingsTest.php
@@ -92,6 +92,7 @@ public function testEntityDisplaySettings() {
       'label' => 'above',
       'type' => 'text_trimmed',
       'settings' => array('trim_length' => 600),
+      'third_party_settings' => array(),
     );
 
     // Make sure we don't have the excluded print entity display.
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldWidgetSettingsTest.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldWidgetSettingsTest.php
index f81fe844b14c..930e77b62dbd 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldWidgetSettingsTest.php
+++ b/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldWidgetSettingsTest.php
@@ -92,6 +92,7 @@ public function testWidgetSettings() {
     $component = $form_display->getComponent('field_test');
     $expected = array('weight' => 1, 'type' => 'text_textfield');
     $expected['settings'] = array('size' => 60, 'placeholder' => '');
+    $expected['third_party_settings'] = array();
     $this->assertEqual($component, $expected, 'Text field settings are correct.');
 
     // Integer field.
diff --git a/core/modules/options/src/Plugin/Field/FieldWidget/OptionsWidgetBase.php b/core/modules/options/src/Plugin/Field/FieldWidget/OptionsWidgetBase.php
index 70f40ec41e86..165bc92d0eb2 100644
--- a/core/modules/options/src/Plugin/Field/FieldWidget/OptionsWidgetBase.php
+++ b/core/modules/options/src/Plugin/Field/FieldWidget/OptionsWidgetBase.php
@@ -45,8 +45,8 @@ abstract class OptionsWidgetBase extends WidgetBase {
   /**
    * {@inheritdoc}
    */
-  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings) {
-    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings);
+  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings) {
+    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
     $property_names = $this->fieldDefinition->getPropertyNames();
     $this->column = $property_names[0];
   }
diff --git a/core/profiles/standard/config/install/entity.form_display.node.article.default.yml b/core/profiles/standard/config/install/entity.form_display.node.article.default.yml
index ebdf6ed2c6b4..359aa183a9c8 100644
--- a/core/profiles/standard/config/install/entity.form_display.node.article.default.yml
+++ b/core/profiles/standard/config/install/entity.form_display.node.article.default.yml
@@ -9,6 +9,7 @@ content:
     settings:
       size: 60
       placeholder: ''
+    third_party_settings: {  }
   body:
     type: text_textarea_with_summary
     weight: 1
@@ -16,20 +17,24 @@ content:
       rows: 9
       summary_rows: 3
       placeholder: ''
+    third_party_settings: {  }
   comment:
     type: comment_default
     weight: 2
     settings: {  }
+    third_party_settings: {  }
   field_tags:
     type: taxonomy_autocomplete
     weight: 3
     settings: {  }
+    third_party_settings: {  }
   field_image:
     type: image_image
     weight: 4
     settings:
       progress_indicator: throbber
       preview_image_style: thumbnail
+    third_party_settings: {  }
 hidden: {  }
 status: true
 dependencies:
diff --git a/core/profiles/standard/config/install/entity.form_display.user.user.default.yml b/core/profiles/standard/config/install/entity.form_display.user.user.default.yml
index 6f0f91f2570e..d93cb2c8a0df 100644
--- a/core/profiles/standard/config/install/entity.form_display.user.user.default.yml
+++ b/core/profiles/standard/config/install/entity.form_display.user.user.default.yml
@@ -8,6 +8,7 @@ content:
     settings:
       progress_indicator: throbber
       preview_image_style: thumbnail
+    third_party_settings: {  }
     weight: -1
 status: true
 dependencies:
diff --git a/core/profiles/standard/config/install/entity.view_display.node.article.default.yml b/core/profiles/standard/config/install/entity.view_display.node.article.default.yml
index 81f0545957e8..15991517c79d 100644
--- a/core/profiles/standard/config/install/entity.view_display.node.article.default.yml
+++ b/core/profiles/standard/config/install/entity.view_display.node.article.default.yml
@@ -5,22 +5,25 @@ mode: default
 status: true
 content:
   field_image:
-    label: hidden
     type: image
+    weight: -1
     settings:
       image_style: large
       image_link: ''
-    weight: -1
-  body:
+    third_party_settings: {  }
     label: hidden
+  body:
     type: text_default
     weight: 0
     settings: {  }
+    third_party_settings: {  }
+    label: hidden
   field_tags:
     type: taxonomy_term_reference_link
     weight: 10
     label: above
     settings: {  }
+    third_party_settings: {  }
 dependencies:
   entity:
     - field.instance.node.article.body
diff --git a/core/profiles/standard/config/install/entity.view_display.node.article.teaser.yml b/core/profiles/standard/config/install/entity.view_display.node.article.teaser.yml
index e21e3cffdcac..d22ab13a5337 100644
--- a/core/profiles/standard/config/install/entity.view_display.node.article.teaser.yml
+++ b/core/profiles/standard/config/install/entity.view_display.node.article.teaser.yml
@@ -5,23 +5,26 @@ mode: teaser
 status: true
 content:
   field_image:
-    label: hidden
     type: image
+    weight: -1
     settings:
       image_style: medium
       image_link: content
-    weight: -1
-  body:
+    third_party_settings: {  }
     label: hidden
+  body:
     type: text_summary_or_trimmed
     weight: 0
     settings:
       trim_length: 600
+    third_party_settings: {  }
+    label: hidden
   field_tags:
     type: taxonomy_term_reference_link
     weight: 10
-    label: above
     settings: {  }
+    third_party_settings: {  }
+    label: above
 dependencies:
   entity:
     - entity.view_mode.node.teaser
diff --git a/core/profiles/standard/config/install/entity.view_display.user.user.compact.yml b/core/profiles/standard/config/install/entity.view_display.user.user.compact.yml
index ef048ea7285d..2b0254295b42 100644
--- a/core/profiles/standard/config/install/entity.view_display.user.user.compact.yml
+++ b/core/profiles/standard/config/install/entity.view_display.user.user.compact.yml
@@ -4,12 +4,13 @@ bundle: user
 mode: compact
 content:
   user_picture:
-    label: hidden
     type: image
+    weight: 0
     settings:
       image_style: thumbnail
       image_link: content
-    weight: 0
+    third_party_settings: {  }
+    label: hidden
 hidden:
   member_for: true
 status: true
diff --git a/core/profiles/standard/config/install/entity.view_display.user.user.default.yml b/core/profiles/standard/config/install/entity.view_display.user.user.default.yml
index 67df43ed7a16..ebf106c8dae6 100644
--- a/core/profiles/standard/config/install/entity.view_display.user.user.default.yml
+++ b/core/profiles/standard/config/install/entity.view_display.user.user.default.yml
@@ -4,12 +4,13 @@ bundle: user
 mode: default
 content:
   user_picture:
-    label: hidden
     type: image
+    weight: 0
     settings:
       image_style: thumbnail
       image_link: content
-    weight: 0
+    third_party_settings: {  }
+    label: hidden
 status: true
 dependencies:
   module:
-- 
GitLab