This is a follow-up bug report to #1535868: Convert all blocks into plugins. I mainly found this when working with the new pluggable block system in contrib.
Drupal\Component\Plugin\Exception\PluginException: The plugin (plugin.core.block.bartik.will_this_crash) did not specify an instance class. in Drupal\Component\Plugin\Factory\DefaultFactory->getPluginClass() (line 60 of /Applications/MAMP/htdocs/drupal-git/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php).
The website has encountered an error. Please try again later.
Steps to Reproduce
- Enable something like Shortcut module.
- Add Shortcuts block somewhere.
- Disable Shortcut module.
- Site is semi-functional, but mostly broken until Shortcut is re-enabled.
A user should be able to disable a module without having to remove block instances first (?).
Comment | File | Size | Author |
---|---|---|---|
#38 | 1881096-interdiff-do-not-test.patch | 1.07 KB | EclipseGc |
#37 | 1881096-37.patch | 7.32 KB | EclipseGc |
#34 | 1881096-34.patch | 6.3 KB | EclipseGc |
#32 | block-1881096-32-tests.patch | 4.62 KB | xjm |
#32 | block-1881096-32.patch | 7.76 KB | xjm |
Comments
Comment #1
tim.plunkettWe already handle uninstall with hook_modules_uninstalled(), we should probably just move everything to the disabled region in hook_modules_disabled().
Also, will be easier with #1871696: Convert block instances to configuration entities to resolve architectural issues
Comment #2
mradcliffeThanks, tim. I think that's the appropriate action to take too.
Here's a throwaway patch with a quick test to demonstrate fail.
Comment #3
mradcliffeOh crap, I forgot about that other change. Never work on dinner and core at the same time. :(
I always fail the first time when I get back to working on core.
Comment #4
xjm#1 sounds sane to me.
Comment #5
sunSounds like another case for #1826602: Allow all configuration entities to be enabled/disabled to me.
Normally, we do not touch the configuration of a module when it is disabled or enabled. If you'd move all block instances of a module to the disabled region, then we're actually destroying the configuration that existed for the disabled module. Thus, for many modules, the net result would be the same as if you had uninstalled the module.
As a result, I think we actually need a 'status' or 'enabled' property in the config of block instances.
Alternatively, the block system should be smart enough to figure out that the plugin-providing module of a block instance is not available and thus silently skip over the instance. That sounds like the proper answer, actually.
Comment #6
xjmHm, I changed my mind actually. #1 is the only reason so far to keep the disabled region, and it would result in having to manually put your blocks back in the right place when re-enabling the module. Edit: It might be okay as a stopgap fix though, given a followup to fix it properly.
This issue is actually a specific case of a more general problem: #1776830: [META-1] Installation and uninstallation of configuration provided by a module that belongs to another module's API.
Comment #7
xjmSo here's one way we could fix this:
config_modules_disabled()
disables the ConfigEntities.Comment #8
xjmOh, I crossposted with @sun.
This is very similar to Views' "broken" handlers... handling, which we're considering removing: #1822048: Introduce a generic fallback plugin mechanism.
Comment #9
sunAFAICS, block plugin instance configurations already contain a "module" key, so slap a
module_exists()
onto that, done?Comment #11
xjmHere's what that would look like. I'm not sure this is the right solution:
block.module
is questionable.block.module
is questionable.Also,
_block_load_blocks()
needs to die.Comment #13
xjmDuh.
Comment #14
tim.plunkettShouldn't there only be one module_exists()? Ideally, around the original throw, so it doesn't have to do config($plugin_id)->get() again. Alternately, we could subclass PluginException.
public function
Comment #15
xjmRemember in IRC when I asked you to kill
_block_load_blocks()
? This is why.Since half of core does this and half does not, I see no reason to until a definitive standard is adopted.
Comment #16
sunMoving the
module_exists()
into a try/catch and conditionally re-throwing the exception is a neat idea. I like it. :)Comment #17
xjmTo clarify for the non-Tims of the world,
_block_load_blocks()
andblock_load()
are not in the same call chain. So, we have to add the same fix twice until the API is cleaned up in #1871696: Convert block instances to configuration entities to resolve architectural issues. :)Comment #18
xjmNow with proper documentation and some more thorough assertions.
Comment #19
xjmUh. Uploaded the wrong patch.
Comment #20
xjmAttached fixes the exception so that it is still thrown when no module key is defined for the config object.
Comment #21
xjmSee: #1881630: [meta] Determine how to respond to invalid plugins (plugin dependencies)
Comment #22
xjmWhat would folks think of using #20 for now to fix the fairly spectacular critical, and then exploring more general solutions and best practices in the related issues? Those being:
Comment #23
chx CreditAttribution: chx commentedCriticals fixed is a good thing in my books.
Comment #24
xjmTo reiterate, the slightly-different exception thrown in
_block_load_blocks()
is a stopgap fix until we kill that function in #1871696: Convert block instances to configuration entities to resolve architectural issues. The patch actually fixes two bugs currently:_block_load_blocks()
to list the block instances.Essentially, the API has two functions that do almost-but-not-quite the same thing, and while both exist, we have to fix the bug by doing almost-but-not-quite the same thing as well. :) This is why we have two separate exceptions and two separate sets of test assertions.
Comment #25
sunIn the old world of Block module, silently skipping over a block like that had pretty critical + destructive (data loss) consequences - are we sure that we're not running into those issues too here?
(The test only goes as far as disabling, but not to the full extent of editing (others) + re-enabling + verifying that things still work.)
I'm relatively sure that @catch will complain about the sprintf() instead of t() here — especially in .module files (contrary to decoupled classes in Drupal\Component), there's actually no reason to not use t().
Comment #26
xjmAll
_block_rehash()
does is fetch a list of existing block instances and sorts them by theme for the admin page; see block_admin_display_prepare_blocks(). Skipping over them to not list them makes sense. The other place_block_rehash()
is used is in hook_rebuild(), and we don't need disabled modules' block instances to be in the cached list since they are disabled.I thought exceptions were not supposed to be translated? I guess we could use
format_string()
. It's going to go away in any case in #1871696: Convert block instances to configuration entities to resolve architectural issues.Comment #27
xjmI agree about adding more test coverage though; that makes sense.
Comment #28
xjmHow's this?
Comment #29
xjmOops, #28 is asserting the wrong block link; I've cancelled its test run.
Attached also submits the block ordering form while the test module is disabled for good measure. If there were any place data loss might occur, I imagine it'd be when submitting the block admin form without the disabled block listed, from a submit handler naïvely saying "delete anything not listed!" (That's not how block instances get deleted, though.)
Comment #30
xjmAnd confirm that the block admin reordering is actually submitted successfully.
Comment #31
xjmRerolling to use #1872540: Provide a test helper method for creating block instances.
Comment #32
xjmAdded additional coverage to ensure that re-enabled modules' block instances go back to the correct regions.
Comment #33
sunLooks great to me. RTBC if bot comes back green.
Comment #34
EclipseGc CreditAttribution: EclipseGc commentedI took my own stab at fixing this through implementation of hook_modules_en/disabled(). I'm using the exact same test that xjm posted in #32.
Comment #35
sunI'm very scared by the idea of nilly-willy renaming configuration objects. The first and foremost thing that will break is configuration staging.
True, configuration of disabled modules is staged as-is currently, without triggering the required API calls to import them. However, that only clarifies how broken disabled modules really are. We'll have to fix #1199946: Disabled modules are broken beyond repair so the "disable" functionality needs to be removed for D8.
Until then, I'd rather go ahead with #32, since it fixes the problem within the scope of the system that has it.
Comment #36
xjmYeah, we'd at least have to add some magic for handling the disabled stuff during import and synch operations, which would mean making that prefix canonical in CMI, and I don't think we could commit something like #34 without CMI supporting it for that reason. E.g., with #34, if you had a
disabled.plugin.core.block.bartik.myblock
, CMI would happily allow you to create anotherplugin.core.block.bartik.myblock
. Then when you re-enable the owner module for the first one, ta-da, data loss. Which brings us back to #1776830: [META-1] Installation and uninstallation of configuration provided by a module that belongs to another module's API.Comment #37
EclipseGc CreditAttribution: EclipseGc commentedOK, so understanding the concern here, how about this modification to xjm's patch? (tests are unaltered)
Eclipse
Comment #38
EclipseGc CreditAttribution: EclipseGc commentedInterdiff
Comment #39
xjmOh. Yeah I am cool with that. Instead of working around
_block_load_blocks()
being special, we make it slightly less special. :) RTBC assuming it passes tests.Comment #41
tim.plunkett#37: 1881096-37.patch queued for re-testing.
Comment #42
xjmComment #43
webchickCommitted and pushed to 8.x. Thanks!
Comment #44
catchThis fix is really messy so while I've marked sun's issue as duplicate I've bumped the original one to critical.
Comment #45
xjmWhat issue do you mean?
#1871696: Convert block instances to configuration entities to resolve architectural issues should help. :(
Comment #46
sun@xjm: #1199946: Disabled modules are broken beyond repair so the "disable" functionality needs to be removed
@catch's comment surely wasn't meant to discredit the good and very smart code of this patch. The focus definitely was on the insanity that we need to care for this and implant runtime conditional code for it in the first place, which is not caused by this issue/patch, nor by the new block plugins, nor by the plugin system, but instead, by the weird feature and possibility of disabled modules only.
Comment #47
catchYes sorry that comment was a bit curt. It's messy that we have to worry about this at all, not this specific patch.
Comment #48
xjm