diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php index 6d5cd5f5a5..8b22314369 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php @@ -2,6 +2,7 @@ namespace Drupal\Core\Config\Entity; +use Drupal\Core\Entity\EntityWithDependentPluginCollectionInterface; use Drupal\Core\Plugin\RemovableDependentPluginInterface; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Cache\Cache; @@ -508,21 +509,76 @@ public function onDependencyRemoval(array $dependencies) { // Allow plugins to react to dependency removal. $plugin_configuration_changed = FALSE; - if ($this instanceof EntityWithPluginCollectionInterface) { - foreach ($this->getPluginCollections() as $plugin_collection) { + if ($this instanceof EntityWithDependentPluginCollectionInterface) { + foreach ($this->getPluginCollections() as $collection_group => $plugin_collection) { foreach ($plugin_collection as $instance) { if ($instance instanceof RemovableDependentPluginInterface) { - if ($instance->onDependencyRemoval($dependencies)) { - $plugin_configuration_changed = TRUE; + // Give instance the opportunity to react on dependency removal. + $instance_removed_dependencies = $this->getPluginRemovedDependencies($instance->calculateDependencies(), $dependencies); + if ($instance_removed_dependencies) { + if ($instance->onDependencyRemoval($instance_removed_dependencies)) { + // Let the config entity react. + $this->onPluginDependencyRemoval($collection_group, $instance); + $plugin_configuration_changed = TRUE; + } + // If there are unresolved deleted dependencies left, allow the + // config entity make a final decision. + if ($this->getPluginRemovedDependencies($instance->calculateDependencies(), $dependencies)) { + $this->onUnresolvedPluginDependencyRemoval($collection_group, $instance); + $plugin_configuration_changed = TRUE; + } } } } } } + return $third_party_settings_changed || $plugin_configuration_changed; } /** + * Returns the plugin dependencies being removed. + * + * The function recursively computes the intersection between all plugin + * dependencies and all removed dependencies. + * + * 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 + * ConfigEntityInterface::calculateDependencies(). + * @param array[] $removed_dependencies + * A list of dependencies having the same structure as the input argument of + * ConfigEntityInterface::onDependencyRemoval(). + * + * @return array + * A recursively computed intersection. + * + * @see \Drupal\Core\Config\Entity\ConfigEntityInterface::calculateDependencies() + * @see \Drupal\Core\Config\Entity\ConfigEntityInterface::onDependencyRemoval() + */ + protected function getPluginRemovedDependencies(array $plugin_dependencies, array $removed_dependencies) { + $intersect = []; + foreach ($plugin_dependencies as $type => $dependencies) { + if ($removed_dependencies[$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() + if (in_array($type, ['config', 'content'])) { + $removed = array_intersect_key($removed_dependencies[$type], array_flip($dependencies)); + } + else { + $removed = array_values(array_intersect($removed_dependencies[$type], $dependencies)); + } + if ($removed) { + $intersect[$type] = $removed; + } + } + } + return $intersect; + } + + /** * {@inheritdoc} * * Override to never invalidate the entity's cache tag; the config system diff --git a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php index b6c73e5550..67a8fc3aeb 100644 --- a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php +++ b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php @@ -503,48 +503,6 @@ public function onDependencyRemoval(array $dependencies) { } /** - * Returns the plugin dependencies being removed. - * - * The function recursively computes the intersection between all plugin - * dependencies and all removed dependencies. - * - * 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 - * ConfigEntityInterface::calculateDependencies(). - * @param array[] $removed_dependencies - * A list of dependencies having the same structure as the input argument of - * ConfigEntityInterface::onDependencyRemoval(). - * - * @return array - * A recursively computed intersection. - * - * @see \Drupal\Core\Config\Entity\ConfigEntityInterface::calculateDependencies() - * @see \Drupal\Core\Config\Entity\ConfigEntityInterface::onDependencyRemoval() - */ - protected function getPluginRemovedDependencies(array $plugin_dependencies, array $removed_dependencies) { - $intersect = []; - foreach ($plugin_dependencies as $type => $dependencies) { - if ($removed_dependencies[$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() - if (in_array($type, ['config', 'content'])) { - $removed = array_intersect_key($removed_dependencies[$type], array_flip($dependencies)); - } - else { - $removed = array_values(array_intersect($removed_dependencies[$type], $dependencies)); - } - if ($removed) { - $intersect[$type] = $removed; - } - } - } - return $intersect; - } - - /** * Gets the default region. * * @return string diff --git a/core/lib/Drupal/Core/Entity/EntityWithDependentPluginCollectionInterface.php b/core/lib/Drupal/Core/Entity/EntityWithDependentPluginCollectionInterface.php new file mode 100644 index 0000000000..fc9a55454a --- /dev/null +++ b/core/lib/Drupal/Core/Entity/EntityWithDependentPluginCollectionInterface.php @@ -0,0 +1,36 @@ +status) && !empty($this->states); } + /** + * {@inheritdoc} + */ + public function onDependencyRemoval(array $dependencies) { + $changed = $this->getTypePlugin()->onDependencyRemoval($dependencies); + // Ensure the parent method is called in order to process dependencies that + // affect third party settings. + return parent::onDependencyRemoval($dependencies) || $changed; + } + } diff --git a/core/modules/workflows/src/WorkflowTypeInterface.php b/core/modules/workflows/src/WorkflowTypeInterface.php index 203e9cbdb4..0fe668547c 100644 --- a/core/modules/workflows/src/WorkflowTypeInterface.php +++ b/core/modules/workflows/src/WorkflowTypeInterface.php @@ -6,7 +6,6 @@ use Drupal\Component\Plugin\DerivativeInspectionInterface; use Drupal\Component\Plugin\PluginInspectionInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Plugin\RemovableDependentPluginInterface; use Drupal\Core\Session\AccountInterface; /** @@ -16,7 +15,7 @@ * The workflow system is currently experimental and should only be leveraged * by experimental modules and development releases of contributed modules. */ -interface WorkflowTypeInterface extends PluginInspectionInterface, DerivativeInspectionInterface, ConfigurablePluginInterface, RemovableDependentPluginInterface { +interface WorkflowTypeInterface extends PluginInspectionInterface, DerivativeInspectionInterface, ConfigurablePluginInterface { /** * Initializes a workflow. @@ -145,4 +144,19 @@ public function buildTransitionConfigurationForm(FormStateInterface $form_state, */ public function getRequiredStates(); + /** + * Informs the plugin that a dependency of the workflow will be deleted. + * + * @param array $dependencies + * An array of dependencies that will be deleted keyed by dependency type. + * + * @return bool + * TRUE if the workflow settings have been changed, FALSE if not. + * + * @see \Drupal\Core\Config\ConfigEntityInterface::onDependencyRemoval() + * + * @todo https://www.drupal.org/node/2579743 make part of a generic interface. + */ + public function onDependencyRemoval(array $dependencies); + }