diff --git a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php index d917975..2f37596 100644 --- a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php +++ b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php @@ -450,6 +450,7 @@ public function onDependencyRemoval(array $dependencies) { if ($renderer->onDependencyRemoval($component_removed_dependencies)) { // Update component settings to reflect changes. $component['settings'] = $renderer->getSettings(); + $component['third_party_settings'] = $renderer->getThirdPartySettings(); $this->setComponent($name, $component); $changed = TRUE; } @@ -477,10 +478,7 @@ public function onDependencyRemoval(array $dependencies) { * The function recursively computes the intersection between all plugin * dependencies and all removed dependencies. * - * Note: - * - The two arguments doesn't have the same structure. - * - $removed_dependencies has already sane defaults. All the types of events - * are filled in, even with empty arrays. + * Note: The two arguments do not have the same structure. * * @param array[] $plugin_dependencies * A list of dependencies having the same structure as the return value of @@ -500,20 +498,11 @@ protected function getPluginRemovedDependencies(array $plugin_dependencies, arra foreach ($plugin_dependencies as $type => $dependencies) { if ($removed_dependencies[$type]) { $is_entity_dependency = in_array($type, ['config', 'content']); - // Build a list of removed dependency names for each dependency type. // Config and content entities have the dependency names as keys while // module and theme dependencies are indexed arrays of dependency names. // @see \Drupal\Core\Config\ConfigManager::callOnDependencyRemoval() - $names = $is_entity_dependency ? array_keys($removed_dependencies[$type]) : $removed_dependencies[$type]; - foreach ($dependencies as $name) { - if (in_array($name, $names)) { - if ($is_entity_dependency) { - $intersect[$type][$name] = $removed_dependencies[$type][$name]; - } - else { - $intersect[$type][] = $name; - } - } + if ($removed = $is_entity_dependency ? array_intersect_key($removed_dependencies[$type], array_flip($dependencies)) : array_values(array_intersect($removed_dependencies[$type], $dependencies))) { + $intersect[$type] = $removed; } } } diff --git a/core/lib/Drupal/Core/Field/PluginSettingsBase.php b/core/lib/Drupal/Core/Field/PluginSettingsBase.php index 7b835b0..7d63348 100644 --- a/core/lib/Drupal/Core/Field/PluginSettingsBase.php +++ b/core/lib/Drupal/Core/Field/PluginSettingsBase.php @@ -97,6 +97,16 @@ public function setSetting($key, $value) { /** * {@inheritdoc} */ + public function getThirdPartySettings($module = NULL) { + if ($module) { + return isset($this->thirdPartySettings[$module]) ? $this->thirdPartySettings[$module] : NULL; + } + return $this->thirdPartySettings; + } + + /** + * {@inheritdoc} + */ public function getThirdPartySetting($module, $key, $default = NULL) { return isset($this->thirdPartySettings[$module][$key]) ? $this->thirdPartySettings[$module][$key] : $default; } @@ -127,10 +137,10 @@ public function calculateDependencies() { */ public function onDependencyRemoval(array $dependencies) { $changed = FALSE; - if (!empty($this->third_party_settings)) { - $old_count = count($this->third_party_settings); - $this->third_party_settings = array_diff_key($this->third_party_settings, array_flip($dependencies['module'])); - $changed = $old_count != count($this->third_party_settings); + if (!empty($this->thirdPartySettings) && !empty($dependencies['module'])) { + $old_count = count($this->thirdPartySettings); + $this->thirdPartySettings = array_diff_key($this->thirdPartySettings, array_flip($dependencies['module'])); + $changed = $old_count != count($this->thirdPartySettings); } return $changed; } diff --git a/core/lib/Drupal/Core/Field/PluginSettingsInterface.php b/core/lib/Drupal/Core/Field/PluginSettingsInterface.php index 127c8dc..5638a7c 100644 --- a/core/lib/Drupal/Core/Field/PluginSettingsInterface.php +++ b/core/lib/Drupal/Core/Field/PluginSettingsInterface.php @@ -82,6 +82,20 @@ public function setSetting($key, $value); public function getThirdPartySetting($module, $key, $default = NULL); /** + * Returns all third-party settings values. + * + * @param string|null $module + * (optional) If passed, only settings provided by that module will be + * returned. If omitted all settings will be returned. Defaults to NULL. + * + * @return array|null + * All third-party settings, if the module is omitted. Only settings + * provided by the module, if a module is passed. If a module has been + * passed but that module has no settings, NULL will be returned. + */ + public function getThirdPartySettings($module = NULL); + + /** * Sets the value of a third-party setting for the plugin. * * @param string $module @@ -96,8 +110,7 @@ public function getThirdPartySetting($module, $key, $default = NULL); public function setThirdPartySetting($module, $key, $value); /** - * Informs the plugin that some configuration it might depend on will be - * deleted. + * Informs the plugin that some configuration it depends on will be deleted. * * This method allows plugins to keep their configuration up-to-date when a * dependency calculated with ::calculateDependencies() is removed. For 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 index 04dc5fe..3712b47 100644 --- 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 @@ -55,6 +55,14 @@ field.widget.settings.test_field_widget_multiple: type: string label: 'Test setting' +field.widget.third_party.color: + type: mapping + label: 'Field test entity display color module third party settings' + mapping: + foo: + type: string + label: 'Foo setting' + field.storage_settings.test_field: type: mapping label: 'Test field storage settings' diff --git a/core/modules/field_ui/src/Tests/EntityDisplayTest.php b/core/modules/field_ui/src/Tests/EntityDisplayTest.php index 2414365..13eb95b 100644 --- a/core/modules/field_ui/src/Tests/EntityDisplayTest.php +++ b/core/modules/field_ui/src/Tests/EntityDisplayTest.php @@ -7,6 +7,7 @@ namespace Drupal\field_ui\Tests; +use Drupal\Component\Utility\FormattableString; use Drupal\Component\Utility\Unicode; use Drupal\Core\Cache\Cache; use Drupal\Core\Database\Database; @@ -510,7 +511,7 @@ public function testGetDisplayModeOptions() { * Tests components dependencies additions. */ public function testComponentDependencies() { - $this->enableModules(['dblog']); + $this->enableModules(['dblog', 'color']); $this->installSchema('dblog', ['watchdog']); $this->installEntitySchema('user'); /** @var \Drupal\user\RoleInterface[] $roles */ @@ -549,8 +550,8 @@ public function testComponentDependencies() { $dependencies = ['user.role.' . $roles[0]->id(), 'user.role.' . $roles[1]->id()]; // The config object should not depend on none of the two $roles. - $this->assertFalse($this->isConfigDependency($dependencies[0], $form_display)); - $this->assertFalse($this->isConfigDependency($dependencies[1], $form_display)); + $this->assertNoDependency('config', $dependencies[0], $form_display); + $this->assertNoDependency('config', $dependencies[1], $form_display); // Add a widget of type 'test_field_widget'. $component = [ @@ -560,23 +561,47 @@ public function testComponentDependencies() { 'role' => $roles[0]->id(), 'role2' => $roles[1]->id(), ], + 'third_party_settings' => [ + 'color' => ['foo' => 'bar'], + ], ]; $form_display->setComponent($field_name, $component); $form_display->save(); // Now, the form display should depend on both user roles $roles. - $this->assertTrue($this->isConfigDependency($dependencies[0], $form_display)); - $this->assertTrue($this->isConfigDependency($dependencies[1], $form_display)); + $this->assertDependency('config', $dependencies[0], $form_display); + $this->assertDependency('config', $dependencies[1], $form_display); + // The form display should depend on 'color' module. + $this->assertDependency('module', 'color', $form_display); // Delete the first user role entity. $roles[0]->delete(); // Reload the form display. $form_display = EntityFormDisplay::load($form_display->id()); + // The display exists. + $this->assertFalse(empty($form_display)); // The form display should not depend on $role[0] anymore. - $this->assertFalse($this->isConfigDependency($dependencies[0], $form_display)); + $this->assertNoDependency('config', $dependencies[0], $form_display); // The form display should depend on 'anonymous' user role. - $this->assertTrue($this->isConfigDependency('user.role.anonymous', $form_display)); + $this->assertDependency('config', 'user.role.anonymous', $form_display); + // The form display should depend on 'color' module. + $this->assertDependency('module', 'color', $form_display); + + // Manually trigger the removal of configuration belonging to the module + // because KernelTestBase::disableModules() is not aware of this. + $this->container->get('config.manager')->uninstall('module', 'color'); + // Uninstall 'color' module. + $this->disableModules(['color']); + + // Reload the form display. + $form_display = EntityFormDisplay::load($form_display->id()); + // The display exists. + $this->assertFalse(empty($form_display)); + // The component is still enabled. + $this->assertNotNull($form_display->getComponent($field_name)); + // The form display should not depend on 'color' module anymore. + $this->assertNoDependency('module', 'color', $form_display); // Delete the 2nd user role entity. $roles[1]->delete(); @@ -601,22 +626,52 @@ public function testComponentDependencies() { } /** - * Returns TRUE if $key is a config dependency of $entity_display. + * Asserts that $key is a $type type dependency of $display config entity. * + * @param string $type + * The dependency type: 'config', 'content', 'module' or 'theme'. * @param string $key * The string to be checked. - * @param \Drupal\Core\Entity\Display\EntityDisplayInterface $entity_display + * @param \Drupal\Core\Entity\Display\EntityDisplayInterface $display * The entity display object to get dependencies from. + */ + protected function assertDependency($type, $key, EntityDisplayInterface $display) { + $this->assertDependencyHelper(TRUE, $type, $key, $display); + } + + /** + * Asserts that $key is not a $type type dependency of $display config entity. * - * @return bool - * If the supplied $key is a config dependency of the $entity_display. + * @param string $type + * The dependency type: 'config', 'content', 'module' or 'theme'. + * @param string $key + * The string to be checked. + * @param \Drupal\Core\Entity\Display\EntityDisplayInterface $display + * The entity display object to get dependencies from. + */ + protected function assertNoDependency($type, $key, EntityDisplayInterface $display) { + $this->assertDependencyHelper(FALSE, $type, $key, $display); + } + + /** + * Provides a helper for dependency assertions. * - * @see testComponentDependencies() + * @param bool $assertion + * Assertion: positive or negative. + * @param string $type + * The dependency type: 'config', 'content', 'module' or 'theme'. + * @param string $key + * The string to be checked. + * @param \Drupal\Core\Entity\Display\EntityDisplayInterface $display + * The entity display object to get dependencies from. */ - protected function isConfigDependency($key, EntityDisplayInterface $entity_display) { - $dependencies = $entity_display->getDependencies(); - $config_dependencies = !empty($dependencies['config']) ? $dependencies['config'] : []; - return in_array($key, $config_dependencies); + protected function assertDependencyHelper($assertion, $type, $key, EntityDisplayInterface $display) { + $all_dependencies = $display->getDependencies(); + $dependencies = !empty($all_dependencies[$type]) ? $all_dependencies[$type] : []; + $value = $assertion ? in_array($key, $dependencies) : !in_array($key, $dependencies); + $args = ['@value' => var_export($value, TRUE)]; + $message = $assertion ? new FormattableString('Value @value is TRUE.', $args) : new FormattableString('Value @value is FALSE.', $args); + $this->assert($value, $message); } }