Problem/Motivation

When a config entity implementing EntityWithPluginCollectionInterface is reacting on dependency removal, it should ask the plugins from the collections if they want to take some action when their dependencies are about to be removed.

This issue is adding a generic solution for the one fixed in #2562107: EntityDisplayBase should react on removal of its components dependencies.

Bumping this to critical, because #3326447: Deleting a Media view mode should not result in the deletion of an entire text format combined with #3537624: Deleting filter formats may result in data loss can lead to content data loss. There is a case of this actually happening brought up in slack: https://drupal.slack.com/archives/C1BB308HH/p1755719629686569

Steps to reproduce

Two example scenarios:

  1. Create a new Image style
  2. Add an image effect from a module other than the core image module to the image style
  3. (optional) Add other effects including from the image module as desired
  4. Save the image style
  5. Uninstall the module provide the image effect in step 2
  6. Observe that the image style has been deleted

and From #3326447: Deleting a Media view mode should not result in the deletion of an entire text format

  1. Install Drupal HEAD with the Standard profile.
  2. Enable the Media and Media Library modules.
  3. On /admin/structure/display-modes/view/add/media/, create a new view mode for Media called "Test" and click save.
  4. On /admin/structure/media/manage/image/display, under "Custom display settings", check "Test" and click save.
  5. On /admin/config/content/formats/manage/basic_html, add the media button to the WYSIWYG toolbar.
  6. Further down the page, check "Embed media".
  7. Further down still, open the "Embed media" vertical tab that becomes available.
  8. Under "View modes selectable in the 'Edit media' dialog", check "Default" and "Test". Click "Save".
  9. Go to /admin/structure/display-modes/view/manage/media.test/delete. It warns you that not only the unused view mode but also the editor config and the basic HTML input format are about to be deleted:
  10. If you don't read carefully, you may end up deleting the entire Basic HTML format, rendering your content both broken and uneditable in the process.

Proposed resolution

Proposed solution (MR 13171)

  1. Add new interface Drupal\Core\Plugin\RemovableDependentPluginInterface with method onCollectionDependencyRemoval()
  2. Add code in ConfigEntityBase::onDependencyRemoval() that tests if the entity implements EntityWithPluginCollectionInterface. If so, iterate through collections and if there are plugins that implement RemovableDependentPluginInterfacedependencies::onCollectionDependencyRemoval(), invoke the method on that plugin so it can react by changing its configuration or removing itself from the collection
  3. Add a new enum RemovableDependentPluginReturn that has three values: Changed, Remove, Unchanged
  4. If onCollectionDependencyRemoval() returns Remove or Unchanged, then ConfigEntityBase::onDependencyRemoval() will return TRUE, so the config entity's dependencies can be recalculated
  5. If onCollectionDependencyRemoval() returns Remove, then remove the plugin from the entity's plugin collection in ConfigEntityBase::onDependencyRemoval()
  6. Implement two use cases: 1) prevent image styles from being deleted if a module that provides any of its image effects is uninstalled and 2) prevent filter formats from being deleted if a media entity view mode used in a media embed filter is deleted
  7. Tests.

(Previous alternate solution)

  1. Add code in ConfigEntityBase::onDependencyRemoval() that tests if the entity implements EntityWithPluginCollectionInterface. If so, iterate through collections and if there are specific plugin dependencies being removed, call the plugin ::onDependencyRemoval() method to allow the plugin to react.
  2. Add a new method onDependencyRemoval() in \Drupal\Component\Plugin\DependentPluginInterface.
  3. Add empty onDependencyRemoval() implementations in base classes implementing DependentPluginInterface with a simple return FALSE;.
  4. Implement one use-case.
  5. Tests.

Remaining tasks

None.

User interface changes

None.

API changes

Plugin classes can implement new interface Drupal\Core\Plugin\RemovableDependentPluginInterface that has this method:

/**
   * Informs the plugin in a collection to act on removal of dependencies.
   *
   * This method allows a plugin instance in a collection to remove dependencies
   * from their configuration. For example, if a plugin integrates with a
   * specific module, it should remove that module from its own configuration
   * when the module is uninstalled.
   *
   * The plugin collection and the index of the plugin instance in the
   * collection are provided in case the removal of dependencies means that the
   * plugin instance should be removed from the collection altogether.
   *
   * @param \Drupal\Component\Plugin\LazyPluginCollection $collection
   *   The plugin collection that contains the plugin instance.
   * @param array-key $index
   *   The index of the plugin instance within the collection.
   * @param array<string, list<string>> $dependencies
   *   An array of dependencies that will be deleted keyed by dependency type.
   *   Dependency types are, for example, entity, module and theme.
   *
   * @return bool
   *   TRUE if the configuration of the plugin instance has changed or if the
   *   plugin instance has been removed from the collection, otherwise FALSE.
   */
  public function onCollectionDependencyRemoval(LazyPluginCollection $collection, string|int $index, array $dependencies): bool;

Data model changes

None.

Issue fork drupal-2579743

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

claudiu.cristea created an issue. See original summary.

claudiu.cristea’s picture

Issue summary: View changes
tim.plunkett’s picture

Issue tags: +API change, +Plugin system
claudiu.cristea’s picture

Assigned: Unassigned » claudiu.cristea

Let's try a first patch.

yched’s picture

1. Add code in ConfigEntityBase::onDependencyRemoval() that tests if the entity implements EntityWithPluginCollectionInterface. If so, iterate through collections and if there are specific plugin dependencies being removed, call the plugin ::onDependencyRemoval() method to allow the plugin to react.

Right, and most of the code currently in EntityDisplayBase::onDependencyRemoval() & the ::getPluginRemovedDependencies() helper can be moved straight up to ConfigEntityBase::onDependencyRemoval() for that.

But the tricky part is then how each specific ConfigEntity class can react to the plugin::onDependencyRemoval() result (whether "OK, I adapted, ask me for my new settings" or "screw it, I'm not changing myself"). AFAICT that logic is specific to the business logic and implementation of each "config entity with a plugin collection" : EntityDisplays do something, others will need something else.

So it seems ConfigEntityBase::onDependencyRemoval() can provide the general mechnism, but will need to defer a separate submethod to handle the result of plugin::onDependencyRemoval(). And that submethod will likely need to be defined in EntityWithPluginCollectionInterface (it only makes sense if the ConfigEntity is a EntityWithPluginCollectionInterface).

Then, same problem as below with adding a method with existing direct implementations, we'll likely need a new subinterface for that...

2. Add a new method onDependencyRemoval() in \Drupal\Component\Plugin\DependentPluginInterface.
3. Add empty onDependencyRemoval() implementations in base classes implementing DependentPluginInterface with a simple return FALSE;.

Problem is, there are no such base classes ? Most implementations of that interface implement it directly, and we can be sure that that is also the case in contrib/custom code out there, and adding that method would break them.

So I fear this means adding a new subinterface of DependentPluginInterface to receive that new method :-/

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

xjm’s picture

Priority: Normal » Major
Issue tags: +DX (Developer Experience)

This has come up again in #2830581: Fix ContentModeration workflow type to calculate correct dependencies.

Bumping to major because this is really confusing and it's hard to realize you need to add such a method to your specific entity type interface, and screwing up your entity's dependency management can cause data integrity issues or data loss, so it would be good to find some solution or mitigation for this.

tim.plunkett’s picture

This would help resolve #1764380: Merge PluginSettingsInterface into ConfigurableInterface, as we need a replacement for \Drupal\Core\Field\PluginSettingsInterface::onDependencyRemoval()

sam152’s picture

Status: Active » Needs review
StatusFileSize
new10.51 KB

Here is an initial version of this. I think the BC concerns are valid, so here is an interface which extends DependentPluginInterface.

I think the meaty tests will come from the implementations of onDependencyRemoval, which are up to the plugins to provide and test. The current test only verifies plugins that implement the interface have a chance to act. Most of the docs on the interface are copied from ConfigEntityInterface::onDependencyRemoval, but should still apply.

tim.plunkett’s picture

Version: 8.3.x-dev » 8.4.x-dev
StatusFileSize
new3.46 KB
new11.35 KB

Great start!
First, due to #2851574: DependentPluginDefinitionInterface and DependentPluginDefinitionTrait should be in Drupal\Core not Drupal\Component we shouldn't repeat the mistakes of core, let's have this live in Drupal\Core.

Second, since PluginSettingsBase already provides implementations of these methods (due to PluginSettingsInterface as mentioned in #10), let's use this new interface there.

Third, the docs on the new RemovableDependentPluginInterface shouldn't mention entities at all. I think this is just because they were copy/pasted as you mentioned.

Finally, we need to untangle \Drupal\Core\Entity\EntityDisplayBase::onDependencyRemoval(). However that may prove trickier than expected, since neither EntityFormDisplay or EntityViewDisplay use EntityWithPluginCollectionInterface correctly...

Here's a patch addressing my first two points.

sam152’s picture

StatusFileSize
new3.78 KB
new13.73 KB

It seemed to make sense to mention their relationship to config entities and how removing dependencies will preserve those entities, but I guess that connection isn't explicit based on the interface.

Fixed the docs and resolved a @todo pointing to this issue. EntityDisplayBase looks like a hairy one, is that in scope for this issue?

Status: Needs review » Needs work

The last submitted patch, 13: 2579743-dependencies-13.patch, failed testing.

claudiu.cristea’s picture

The relevant part from @yched's comment:

Right, and most of the code currently in EntityDisplayBase::onDependencyRemoval() & the ::getPluginRemovedDependencies() helper can be moved straight up to ConfigEntityBase::onDependencyRemoval() for that.

But the tricky part is then how each specific ConfigEntity class can react to the plugin::onDependencyRemoval() result (whether "OK, I adapted, ask me for my new settings" or "screw it, I'm not changing myself"). AFAICT that logic is specific to the business logic and implementation of each "config entity with a plugin collection" : EntityDisplays do something, others will need something else.

So it seems ConfigEntityBase::onDependencyRemoval() can provide the general mechnism, but will need to defer a separate submethod to handle the result of plugin::onDependencyRemoval(). And that submethod will likely need to be defined in EntityWithPluginCollectionInterface (it only makes sense if the ConfigEntity is a EntityWithPluginCollectionInterface).

EDIT: Short version:

  • The "removed dependency fix" cannot be handled in a generic way. It depends on the business logic of that specific config entity.
  • The helper EntityDisplayBase::getPluginRemovedDependencies() should be moved up and reused.
  • Create a submethod to be called by onDependencyRemoval() to perform the actual fixing. That method should be part of EntityWithPluginCollectionInterface (or an extension for BC reasons). Each plugin will provide there the actions to be taken for fixing the config entity when plugin dependencies are removed.
sam152’s picture

Status: Needs work » Needs review

Thanks for summarising that, I think step 1 and 3 are already done in #13.

From what I can tell, getPluginRemovedDependencies is used to detect if a plugin couldn't handle removing it's dependencies. This happens so that the display can just disable the component. I think it's pretty unique for a config entity to understand how to disable some part of itself based on a plugin having unresolved dependencies, it seems like the most most complex use case to the point where I don't know if it's worth using getPluginRemovedDependencies elsewhere? All removed dependencies can be passed to all plugins, I don't see a need for them to be filtered in advance.

Re: suggestion of untangling EntityDisplayBase::onDependencyRemoval in #12, not sure how to go about that. I don't know if it makes sense for a plugin to manage and remove dependent config, because each one isn't responsible for it's own isolated tree of config that it can deal with. A plugin can't move itself from 'content' to 'hidden'. Keen to here if @tim.plunkett has any thoughts on how to approach this.

NR for random fail.

claudiu.cristea’s picture

+++ b/core/lib/Drupal/Core/Plugin/RemovableDependentPluginInterface.php
index c6b1ce6..330c392 100644

--- a/core/modules/workflows/src/Entity/Workflow.php
+++ b/core/modules/workflows/src/Entity/Workflow.php

+++ b/core/modules/workflows/src/Entity/Workflow.php
index 0fe6685..203e9cb 100644

--- a/core/modules/workflows/src/WorkflowTypeInterface.php
+++ b/core/modules/workflows/src/WorkflowTypeInterface.php

On order to keep the clarity, I think we should not mix here any use-case.

claudiu.cristea’s picture

Assigned: claudiu.cristea » Unassigned
claudiu.cristea’s picture

StatusFileSize
new16.11 KB
new8.72 KB

EDIT: Moved the comment to #21

Status: Needs review » Needs work

The last submitted patch, 19: 2579743-19.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new17.51 KB
new10.12 KB

Sorry, the patch from #19 was malformed. Interdiff to #13.

This is only a PoC.

  • I tried to make generic what we have in EntityDisplayBase::onDependencyRemoval().
  • I could not add new methods to EntityWithPluginCollectionInterface. It would break BC.
  • I wasn't able to make EntityDisplayBase to be a use-case because EntityDisplayBase has its own way to deal with plugin collections. If we fix this issue we should open another one to make EntityDisplayBase use this (it will be tricky!).
  • No tests yet.
  • EntityWithPluginCollectionInterface should be deprecated in favour of EntityWithDependentPluginCollectionInterface.

Status: Needs review » Needs work

The last submitted patch, 21: 2579743-21.patch, failed testing.

sam152’s picture

Is there a concrete use case for onUnresolvedPluginDependencyRemoval? It seems like something unique to entity displays, based on their ability to disable fields.

Also, I think it makes sense to actually use the interface outside of a test when it's introduced. The deletions show an example of what the interface gives you free.

claudiu.cristea’s picture

StatusFileSize
new4.67 KB
new4.67 KB

The new methods should return if the change was done or not. The tests are still not fixed.

Is there a concrete use case for onUnresolvedPluginDependencyRemoval? It seems like something unique to entity displays, based on their ability to disable fields.

If a plugin is not able to fix the removed dependencies, the host entity will be removed. In a lot of cases we want to avoid this. This provides a generic way to do something else than let the entity die. For example, some entities will want to remove the plugin from collection, others will decide to replace the plugin with a fallback plugin or will decide to disable themselves (like the entity display does).

claudiu.cristea’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 24: 2579743-24.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new19.64 KB
new4.67 KB

Again wrong patch. Sorry!

Status: Needs review » Needs work

The last submitted patch, 27: 2579743-27.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new25.77 KB
new10.54 KB

Here's the test fix.

claudiu.cristea’s picture

StatusFileSize
new24.87 KB
new2.87 KB

Fixing test. Reverting PluginSettingsBase for now.

The last submitted patch, 29: 2579743-29.patch, failed testing.

tim.plunkett’s picture

#2830581: Fix ContentModeration workflow type to calculate correct dependencies is now in, so we should address that introduction of onDependencyRemoval() as well.

claudiu.cristea’s picture

StatusFileSize
new27.28 KB
new5.46 KB

Here's a try with ImageStyle and the effects plugin collection. But It needs a nice Kernel test.

Modified the EntityWithDependentPluginCollectionInterface::onPluginDependencyRemoval and ::onUnresolvedPluginDependencyRemoval signatures to take also the list of removed dependencies as argument.

claudiu.cristea’s picture

@tim.plunkett, @Sam152, I think right now we need a partial (more architectural) review about the direction. Then we need to refresh the IS based on the conclusions.

Status: Needs review » Needs work

The last submitted patch, 33: 2579743-33.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new28.02 KB
new1.66 KB

Dammit!

Status: Needs review » Needs work

The last submitted patch, 36: 2579743-36.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review
StatusFileSize
new28.06 KB
new1.02 KB

huh?

Status: Needs review » Needs work

The last submitted patch, 38: interdiff-36-to-38.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review
tim.plunkett’s picture

+++ b/core/modules/image/src/Entity/ImageStyle.php
@@ -407,8 +407,16 @@ public function onPluginDependencyRemoval($collection_group, PluginInspectionInt
+        $this->getEffects()->setInstanceConfiguration($plugin->getPluginId(), $plugin->getConfiguration());

This is handled in ConfigEntityBase::preSave(), do we really need it again here?

claudiu.cristea’s picture

This is handled in ConfigEntityBase::preSave(), do we really need it again here?

I missed that, thank you. I don't know :)

tim.plunkett’s picture

I think we should switch to a TDD approach here, it will help avoid adding extra code. Let's define what the goals are, write test coverage for it, and then adjust the internals as needed.

claudiu.cristea’s picture

@tim.plunkett, here's a proposal:

  1. Config entities with plugin collections should automatically ask the plugins to update when their dependencies are removed.
  2. If a plugin instance has been changed due to its dependency removal, the config entity should be able to update too (new method).
  3. The config entity should be able to react in the case of unresolved dependencies still left after the plugin has updated (new method).
  4. Write unit tests to cover ConfigEntityBase.
  5. Pick-up and fix a use case of such entity (can be \Drupal\workflows\Entity\Workflow) and adapt to the new API. Write kernel test for this use-case.
  6. Open followups to fix all config entities with plugin collections from core.
  7. Follow-up to refactor EntityDisplayBase for a proper usage of plugin collections.
sam152’s picture

Status: Needs review » Needs work

This all looks pretty good, but I think the onPluginDependencyRemoval method needs to be removed from the picture. I don't think it does anything in the example in the patch and I don't see why anything it could conceivably do couldn't be part of the method for the individual plugin. The "let the config entity react" comment isn't really needed if the point was to refresh the config entity, as @tim.plunkett suggested that's the job of the collection.

NW for that + @todos.

  1. +++ b/core/modules/image/src/Entity/ImageStyle.php
    @@ -399,6 +400,40 @@ public function setName($name) {
    +      if (in_array($plugin->getPluginDefinition()['provider'], $dependencies['module'])) {
    +        // If the module providing the effect is disabled, remove the effect
    +        // from this image style and exit here.
    +        $this->deleteImageEffect($plugin);
    +        return TRUE;
    +      }
    

    Wouldn't this already happen because the image style fails to remove the module as a dependency and onUnresolvedPluginDependencyRemoval removes it?

claudiu.cristea’s picture

Assigned: Unassigned » claudiu.cristea

Trying to work on this weekend.

sam152’s picture

Hi claudiu.cristea, are you still actively working on this?

claudiu.cristea’s picture

Assigned: claudiu.cristea » Unassigned
sam152’s picture

Status: Needs work » Needs review
StatusFileSize
new9.73 KB
new24.78 KB

I've tried to jump start this up again by rerolling and implementing some of #45.

I don't really understand the test cases or the fails, mocking them for specific return values on a specific number of calls is hard to follow but will find some time to have a look at it all again.

sam152’s picture

Status: Needs review » Needs work
sam152’s picture

+++ b/core/lib/Drupal/Core/Entity/EntityWithPluginCollectionInterface.php
@@ -7,9 +7,12 @@
+ * @deprecated Scheduled for removal before  Drupal 9.0.0. Use
+ *   \Drupal\Core\Entity\EntityWithDependentPluginCollectionInterface instead.

I think we also need to check the BC policy, I think interface additions when there is a suitable base class are allowed.

claudiu.cristea’s picture

This all looks pretty good, but I think the onPluginDependencyRemoval method needs to be removed from the picture

This is wrong for the reasons you can find by analysing and understanding how EntityDisplayBase::onDependencyRemoval() works. Actually, that is a particular case that we need to expand in a general case here. Also, I've explained that in #44.2 and 3.

EDIT: The test \Drupal\Tests\field_ui\Kernel\EntityDisplayTest::testComponentDependencies(), that I wrote when fixing EntityDisplayBase, is a case that reflect what we need to solve here. There's also a relevant comment in \Drupal\field_test\Plugin\Field\FieldWidget\TestFieldWidget::onDependencyRemoval().

EDIT2: So, there are 2 kind of reactions that the entity should be provided. First, when dependency removal are resolved and second, when there are still left unresolved dependencies after the first step.

sam152’s picture

#44 doesn't have an explanation.

I don't think the pattern of the config entity having specific awareness of the plugins private configuration (and thus changes when the plugin does) is a pattern that should be encouraged by the interfaces, no matter what is in EntityDisplayBase. Generally speaking, the plugin configuration should be self-contained (you know, pluggable...) and there shouldn't typically be much reason for a config entity to change when a plugin cleanly resolves a dependency removal. The plugin config should be encapsulated and irrelevant to the config entity.

Perhaps the patch should have a concrete example of where this method makes sense instead of shoehorning it into ImageStyle. You could remove that whole method from that entity and it would behave the same.

Maybe step 7 should be resolved before core APIs are written to cater for it:

#44.7 Follow-up to refactor EntityDisplayBase for a proper usage of plugin collections.

claudiu.cristea’s picture

Assigned: Unassigned » claudiu.cristea
Issue tags: +Needs issue summary update

Discussed with @tim.plunkett on IRC and we agreed for the next plan:

  • Convert EntityDisplayBase to properly use plugin collections.
  • Implement #44.1-3.

This IS will be updated accordingly. I will handle these tasks.

sam152’s picture

Is there an issue for fixing EntityDisplayBase?

claudiu.cristea’s picture

Issue tags: +DCTransylvania

@Sam152, will be handled here.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

smustgrave’s picture

Status: Needs work » Postponed (maintainer needs more info)

Wonder if someone could provide an updated issue summary? Been about 5 years so a lot has changed.

andypost’s picture

I bet it's about to kinda "behavior.detach()" to allow plugins manage destruction for example

andypost’s picture

Version: 9.5.x-dev » 10.1.x-dev
Assigned: claudiu.cristea » Unassigned
Status: Postponed (maintainer needs more info) » Needs work

it still very handy to have this reaction, it moves forward action plugins at least

+++ b/core/lib/Drupal/Core/Plugin/RemovableDependentPluginInterface.php
@@ -0,0 +1,42 @@
+ * Provides an interface for plugins that react when dependencies are removed.
...
+interface RemovableDependentPluginInterface extends DependentPluginInterface {
...
+   * If this method returns TRUE then the entity the plugin is attached to needs
+   * to be re-saved by the caller for the changes to take effect.
...
+   * @param array $dependencies
+   *   An array of dependencies that will be deleted keyed by dependency type.
+   *   Dependency types are content, config, module and theme.
...
+   * @return bool
+   *   TRUE if the entity has been changed as a result, FALSE if not.
...
+  public function onDependencyRemoval(array $dependencies);

It still makes sense as separate interface

ravi.shankar’s picture

StatusFileSize
new23.9 KB
new9.19 KB

Added reroll of patch #49 on Drupal 10.1.x. still needs work for comment #69.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

godotislate made their first commit to this issue’s fork.

godotislate’s picture

Priority: Major » Critical
Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -Needs issue summary update +Needs tests

Created MR 13171 with a new approach that is mostly based on the MR for #3326447: Deleting a Media view mode should not result in the deletion of an entire text format, combined with some ideas from the patch in #70:

  1. Add new interface Drupal\Core\Plugin\RemovableDependentPluginInterface with method onCollectionDependencyRemoval()
  2. Add code in ConfigEntityBase::onDependencyRemoval() that tests if the entity implements EntityWithPluginCollectionInterface. If so, iterate through collections and if there are plugin that implement RemovableDependentPluginInterfacedependencies::onCollectionDependencyRemoval(), invoke the method on that plugin so it can react by changing its configuration or removing itself from the collection
  3. If onCollectionDependencyRemoval() returns TRUE, then ConfigEntityBase::onDependencyRemoval() will return TRUE, so the config entity's dependencies can be recalculated
  4. Implement two use cases: 1) prevent image styles from being deleted if a module that provides any of its image effects is uninstalled and 2) prevent filter formats from being deleted if a media entity view mode used in a media embed filter is deleted
  5. Add tests for two use cases

This approach kind of comes at the problem from the other way of the patch. The handling of the dependency removal is done completely in the plugin class, other than the entity invoking the plugin's onCollectionDependencyRemoval() method. The plugin collection and index ID of the plugin instance in the collection are provided as arguments along with the dependencies, so that the plugin instance can be removed from the collection as needed. This approach is kind of based on how Drupal\views\Entity\View\onDependencyRemoval() invokes DependentWithRemovalPluginInterface::onDependencyRemoval() for handler plugins, as well as FieldItemInterface::onDependencyRemoval(), which has the field definition object as well as the dependencies passed in.

There may need to be tests added to test the dependency removal handling generically. The tests in the MR are a kernel test and functional test specific to the the image style and media view mode use cases. The unit tests in #70 no longer apply to the solution.

Also bumping this to critical, because #3326447: Deleting a Media view mode should not result in the deletion of an entire text format combined with #3537624: Deleting filter formats may result in data loss can lead to content data loss. There is a case of this actually happening brought up in slack: https://drupal.slack.com/archives/C1BB308HH/p1755719629686569

Also, with this #3326447: Deleting a Media view mode should not result in the deletion of an entire text format is effectively a duplicate, so it perhaps can be closed with credits transferred here.

godotislate’s picture

Changed the functional test for media to a kernel test, and added a unit test for more generic coverage.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new91 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

godotislate’s picture

Status: Needs work » Needs review
Issue tags: +Needs CR

Rebased. Also tagging for Needs CR to document new interface if we go that way.

godotislate’s picture

Issue summary: View changes
alexpott’s picture

I like the new approach from #75. This is looking good. I left one question on the MR. I also like that we've got two use-cases in core addressed in the MR and there are likely to be more in core and contrib.

godotislate’s picture

Issue tags: -Needs CR

Addressed MR comments. Also added the CR: https://www.drupal.org/node/3549101

godotislate’s picture

Issue summary: View changes
godotislate’s picture

Issue summary: View changes
godotislate’s picture

Issue summary: View changes
godotislate’s picture

Issue summary: View changes
nicxvan’s picture

Status: Needs review » Reviewed & tested by the community

Test coverage looks good. The only thing I didn't like at first was the name, but it follows the established patterns set so not much to suggest beyond that?

The default setting gave me pause because cleaning up new dependency chains can be complex, but contrib can calculate those. I think the only open question is what happens if a method doesn't properly reset dependencies. I assume just what happens now which is it will remove everything. I don't think it's worth holding up over that since it's a much better situation and fixes a data loss bug.

Read through the CR and it seems clear too.

I also read through the IS and it seems up to date and the history, but focused more on the newer comments since the change in direction.

alexpott’s picture

Status: Reviewed & tested by the community » Needs review

I've added an API change to consider before we merge this.

godotislate’s picture

Issue summary: View changes
godotislate’s picture

Made changes per #87 to the MR and updated IS and CR. This is ready again.

godotislate’s picture

Just wanted to make sure to note #3326447: Deleting a Media view mode should not result in the deletion of an entire text format is a duplicate at this point and should be closed with credits transferred here.

nicxvan’s picture

Status: Needs review » Reviewed & tested by the community

I had a look, I think the feedback has been addressed. Name makes sense, left a comment on the MR about a discussion I had with @godotislate. I think this is ready again!

  • alexpott committed 7c964eeb on 11.x
    Issue #2579743 by yched, sam152, claudiu.cristea, tim.plunkett,...
alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed 7c964ee and pushed to 11.x. Thanks!

Given we have new interfaces and an API I've only put this in 11.x so it'll be part of 11.3.0

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

nicxvan’s picture

Thanks! I took a pass at credit since I don't think it saved properly.

Ok try to look at the related issues soon.

claudiu.cristea’s picture

Nice that this has been finally fixed. I wonder if #2562107: EntityDisplayBase should react on removal of its components dependencies could be reworked to use this standard approach.

Status: Fixed » Closed (fixed)

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