Problem/Motivation
If you define a Display Extender plugin in a module and enable it, then uninstall that module, you are unable to load any pages. You get a "Drupal\Component\Plugin\Exception\PluginNotFoundException" error.
The same thing will occur if a module defines an input filter and then that module is uninstalled. If you go to a page with processed text using a format that filter was enabled in, then it will error out with a plugin not found exception.
While we can check uninstall requirements on a per-module basis, having core do this would make much more sense and prevent the dreaded WSOD as more modules get tried and uninstalled.
Proposed resolution
Simplest fix would be in DisplayPluginBase.php:165:
if ($plugin = $manager->createInstance($extender)) {
Change to:
if ($manager->hasDefinition($extender) && $plugin = $manager->createInstance($extender)) {
The other, more involved but possible better option would be to define a default fallback plugin for display extenders, and possibly any other Views plugins that can be defined and cause this problem.
What may be the best solution is to include these plugins, along with any others in the uninstall validator. Perhaps a new method in DefaultPluginManager (or even a trait for plugin managers to include) that allows each plugin manager to perform a check if it can be safely removed or not.
eg for the views handler:
public function uninstallRequirements($module_name) {
foreach ($definitions as $definition) {
if ($definition['provider'] == $module_name && $config->get('views.settings.display_extenders' . $definition['id'])) {
//Throw an exception or return an explanation of why this module can not be uninstalled.
}
}
}
For Filters it would have to loop through each formatter config entity and check if that filter is enabled.
Recommended workaround
Until this bug is fixed, any module implementing display extenders should implement hook_uninstall() to require removing dependencies on it from existing views.
Remaining tasks
| Task | Novice task? | Contributor instructions | Complete? |
|---|---|---|---|
| Add automated tests | Instructions | ||
| Add steps to reproduce the issue | Instructions |
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|
Issue fork drupal-2635728
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
Comment #2
cilefen commentedAre things ok if you disable the display extender thingy (is disabling something they can do?)?
I ask because the module creating the Display Extender or Views could implement the ModuleUninstallValidator which would prevent the module providing the extender from being uninstalled and explain why.
Comment #3
Jamie Holly commentedDisabling it in the Views advanced settings does allow it to uninstall without issue.
The Uninstall validator is a good idea, but that should most likely be handled in core.
I also noticed the same thing happens on input filters that a module defines (hence the title change). If a module defines an input filter and that module is then disabled without removing the filter from any formats that use it, then any formats using that filter will error out when you try to display them.
I think a better approach would be to figure out any plugins that can cause this unexpected behavior and add a check in for the uninstall validator. This is something that will pretty much be a requirement of any modules defining Views extenders, Input filters and possibly many more plugins, so leaving it up to core to decide is the best approach IMHO.
Comment #4
dawehnerOh yeah I think
\Drupal\views\Plugin\views\display\DisplayPluginBase::calculateDependenciesneeds to have some logic around display extenders as well.Ideally the display extender for itself then would figure out whether it changes the behaviour of a particular view and in that case add itself as module dependency.
Comment #5
catchComment #6
frobShould there be an added 'Disabled' mode that disables all plugins that are provided by a module?
Comment #7
dawehnerHere is an example.
Comment #8
geertvd commentedAdded tests exposing the problem
Comment #9
cilefen commentedThere is some unwanted white space here, which can be fixed in the next patch.
Comment #12
alexpottI don't think this is the right solution... I think that we need a views_modules_uninstalled hook implementation that checks if the module being uninstalled provides a display extender and remove it from views.settings::display_extenders and also clean up any views which have configuration for that display extender.
Comment #13
eelkeblokIsn't this fundamentally a question of how we want this handled from the user's perspective? It seems rather "harsh" to essentially break a user's configuration (i.e. remove everything that prevents the module from uninstalling cleanly; [IUUC, this is what catch is suggesting]). Shouldn't this be something the user more consciously decides to do? When you disable a module that is a dependency for other modules, that doesn't disable the dependent modules, it simply prevents the module from being disabled. It is up to the user to change their system in such a way that it is possible to cleanly disable/uninstall the module in a way that is well understood and planned for. I think that rational should ideally be extended to other dependency issues (such as Views Display Extender plugins or input filters).
Comment #14
cilefen commentedSo, what I wrote in #2?
Comment #15
eelkeblokWell, yes, although I think it's important to first agree on the intended outcome (user must take action to be able to cleanly disable a module, vs. automatically trying to do that cleaning).
Comment #16
alexpottWith #2 it would never be possible to uninstall a module that provided a display extender because afaik display extenders don't actually have to be configured in a view - they might just act using the
views.settings::display_extenderssetting.Comment #17
catchDo we tell people what happens on the uninstall form for this? If so that counts as giving people a choice for me.
Comment #18
frobIs there also an issue when configuration is imported and the module/plugin isn't enabled?
Comment #19
alexpottI've got a question about the comment #2 which claims we have similar problems with input filter plugins.
Drupal\filter\FilterUninstallValidatorshould prevent this from happening. If we can confirm the input filters are an issue I think we should open an new issue about input filters specifically. I've tried to reproduce this by doing this:$settings['extension_discovery_scan_tests'] = TRUE;to settings.phpWith regards to display extenders I think we should implement a ModuleUninstallValidatorInterface that prevents modules that provide display extenders that are in used from being uninstalled.
Comment #20
hass commented#2639336: filter.format.php_code already exists in active configuration may be such a filter issue...
Comment #21
dawehnerJust a general idea: Allow display extenders to define whether they apply to a display. When they do, we could add it to a list of them on the display and leverage that on runtime and for config dependencies. Here is a rough start of some patch to support it.
Comment #22
jibranComment #24
sidharrell commentedUpdated issue summary
Comment #25
sidharrell commentedUpdated issue summary.
Working on steps to reproduce.
Comment #26
sidharrell commentedremoving novice task tag from issue summary
Comment #27
lokapujyaWhat module provides a display extender?
The test module: views_test_data has one.
Comment #28
alexpottDiscused with @effulgentsia, @xjm and @cottser. This was made critical in #5 due to the claims of an issue with filter plugins in #3. I investigated the claims in #19 and since then no one has replied proving the claims of #3. Therefore I'm downgrading to major since view's display extenders are strange plugins and there is an acceptable work around of just implementing hook_uninstall() in the couple of modules that use them.
If someone does find a critical issue relating to filter plugins please file a new issue.
Comment #30
jibranHere is the reroll.
Comment #34
cilefen commented@alexpott: I just noticed in #19 you referred to #2 but what you meant was #3. But you referenced #3 in subsequent comments, so it pays to keep reading...
Comment #35
xjm@dawehner, @tim.plunkett, @alexpott, @cilefen, and I discussed this issue awhile back and agreed that it is a major bug. The bug causes fatal errors in the user interface and data integrity/uninstallability problems for a fully supported API. As explained in #28, it's not critical because display extenders are, in general, a limited/special usecase.
We recommend that any module that provides a display extender implement
hook_uninstall()to require removing any dependencies on it in the meanwhile.Comment #37
johnpitcairn commentedRegarding filter plugins - we have a module that added a filter plugin to try to solve a specific problem. We solved that in another way, and would now like to remove the filter from the module without uninstalling the module. We have disabled all instances of the filter in all text formats, but removing the filter plugin from the module results in broken output wherever a text format is used:
Happy to open a new issue about this, but really I'd just like to know what we should do in this situation to allow us to remove the unused filter plugin from the module.
Comment #38
dawehner@John Pitcairn
Please open its own issue for that. Its a bit unrelated to this one, to be honest. Thank you
Comment #40
alan d. commentedIt would be nice if ViewsPluginManager implemented FallbackPluginManagerInterface as suggested in the proposed resolution too.
A much nicer developer experience being able to rename plugins on the fly without reinstalling modules ;)
Comment #43
socialnicheguru commentedcan this be re-rolled for Drupal.8.6.13.
I was using socrata_views which added socrata_views_extender.
It uses hook_uninstall
However uninstalling the module does not solve the issue.
Comment #44
cilefen commentedComment #45
socialnicheguru commentedI am on 8.6.13 and the patch does not apply:
git apply 2635728-44.patch
warning: core/modules/views/src/Plugin/views/display/DisplayPluginBase.php has type 100755, expected 100644
error: patch failed: core/modules/views/src/Plugin/views/display/DisplayPluginBase.php:565
error: core/modules/views/src/Plugin/views/display/DisplayPluginBase.php: patch does not apply
patch -p1 < 2635728-44.patch
patching file core/modules/views/config/schema/views.data_types.schema.yml
patching file core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
Hunk #1 succeeded at 464 (offset 1 line).
Hunk #2 FAILED at 566.
1 out of 2 hunks FAILED -- saving rejects to file core/modules/views/src/Plugin/views/display/DisplayPluginBase.php.rej
patching file core/modules/views/src/Plugin/views/display_extender/DisplayExtenderPluginBase.php
Comment #47
cilefen commentedI disagree:
Comment #49
ghost of drupal pastWe just hit this problem, if you need to fix your site, here's a quick script, run it with
drush scr:I manually edited views.settings.yml and then ran drush cim. I will the read the issue later and see whether I can help but wanted to put this out there just in case someone needs help.
Comment #54
smustgrave commentedRunning #44 for the 9.4.x to see if any test failures happen. Confirmed the patch does apply cleanly.
Is there a good way to help test this one?
Comment #55
smustgrave commentedThink this still needs work. After applying the patch and trying to do a drush cim I get
Invalid data type in config views.data_types.schema, found in file core/modules/views/config/schem
a/views.data_types.schema.yml: Duplicate key "display_extenders" detected at line 261.
Comment #56
smustgrave commentedComment #58
catchThere are several hundred sites that need to uninstall the views_ajax_get module in 10.1. It doesn't implement the hook_uninstall() workaround. Bumping to critical since this leaves sites hosed.
Comment #59
tyler36 commentedThis bug prevents Gutenberg module from being uninstalled when using webprofiler module. See #3261040
Comment #60
acbramley commentedTriaged as part of BSI. This definitely still seems valid but we need:
1. A reroll onto an MR.
2. Tests
3. An update to the IS as it still mentions filter plugins which seem to not be an issue as per #28
Comment #63
tobiasbI do not understand, whether is missing todo or not. just with the patch views does not add the dependency via \Drupal\views\Plugin\views\display\DisplayPluginBase::calculateDependencies. It does also not call \Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase::applies.
There is a plugin_type display_extender in \Drupal\views\Plugin\views\display\DisplayPluginBase::getAllPlugins, but always NULL, when used in \Drupal\views\Plugin\views\display\DisplayPluginBase::calculateDependencies.
I believe we need something like #2426607: Calculates and adds dependencies of views display extender, which use \Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase::applies.
Comment #64
tobiasbOk, as mention in #2426607: Calculates and adds dependencies of views display extender a dependency is not a option because this would delete the view.
Therefore -> views_modules_uninstalled clean-up the config and we will verify that the plugin does exits before we use it.
The clean up in the view display itself is done by the next save of the display.
Comment #66
mohit_aghera commentedI evaluated two solutions for the issue.
hook_modules_uninstalledhookModify/cleanup the views in
hook_modules_uninstalledhook- I feel this might not work in this case.
Reason is, that we can't fetch the list of all the display handler plugins provided by the module being uninstalled.
In the uninstall() function, this hook is the last one to get called https://git.drupalcode.org/project/drupal/-/blob/11.x/core/lib/Drupal/Co...
Since plugin ID or anything won't be available here, we can't identify which display handler plugins we want to unset from views config.
I evaluated another option to check the plugin by doing namespace-based lookup, however, that seemed a bit hacky to me.
Considering that, it might not be straightforward to update all the views and unset the display handler plugins.
We can certainly disable the plugin in views > Advanced config settings.
Implement a new module uninstall validator for the display handler plugins Comment #19
This worked fine and prevented module being uninstalled.
I added on validator plugin along with kernel test to validate the exception.
This seems to be working fine.
I feel at this point we should go ahead with the approach where we display a warning rather than silently updating the views config.
Once we have the consensus around the solution, I can update the issue summary with the necessary details.
Comment #67
mohit_aghera commentedComment #68
smustgrave commentedLeft some comments on the MR.
Comment #71
xjmAttributing according to triage list in #28