Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
The menu system supports unrouted URLs (both external links as well as non-existing pages).
The "Manage" section of the toolbar is built from the Administration menu, and therefore should support unrouted URLs.
It does not.
Additionally, this bug is masked because altering the Administration menu will not clear the cached data for the menu tree used by toolbar.
Proposed resolution
Support unrouted URLs.
Properly cache the menu tree data by passing in a valid menu name.
Remaining tasks
N/A
User interface changes
N/A
API changes
N/A
Data model changes
N/A
Beta phase evaluation
Issue category | Bug because uncaught exceptions are always bugs |
---|---|
Issue priority | Critical because a UI configuration should never, ever lead to an unrecoverable site (and if it requires manual DB work, that's close enough to unrecoverable) |
Comment | File | Size | Author |
---|---|---|---|
#47 | 2532490-toolbar-47.patch | 7.29 KB | tim.plunkett |
#47 | interdiff.txt | 3.3 KB | tim.plunkett |
#41 | 2532490-toolbar-40.patch | 6.73 KB | tim.plunkett |
#38 | interdiff.txt | 1.41 KB | tim.plunkett |
#38 | 2532490-toolbar-37.patch | 6.69 KB | tim.plunkett |
Comments
Comment #1
dawehnerLet's ensure we write test coverage first. Having such a user input flow is not that uncommon.
Comment #2
Crell CreditAttribution: Crell at Palantir.net commentedI'm not even sure how to write a test for this. I can't really generate YAML files to produce the bad configuration since the site is unusable once it's configured that way. (Plus, one of them is a content entity.) Is it reasonable to write a BrowserTest to attempt to simulate pushing all the buttons? Or do we just go spelunking deeper into the system when broken to figure out (and fix, and test) the underlying issue?
Comment #3
dawehnerWell, yeah, why not?
Once we have found a fix we can write an additional test on the lower level system where we fix the actual issue.
Comment #4
catchA before/after database dump ought to flush out the actual issue assuming it's a reference elsewhere in the database.
Also can you confirm rebuild.php definitely does not restore this? Issue summary isn't explicit.
Comment #5
tim.plunkettLooking into this.
I reproduced, and can confirm that rebuild.php does not help.
Comment #6
dawehnerWorking on a test only.
Comment #7
tim.plunkettSteps 2 and 3 (the Views parts) are a red herring.
Views helps because it clears way too many caches when a view is saved.
But doing just Step 1 and hitting rebuild.php should trigger it just the same.
Problem 1: The toolbar cache isn't being updated when the menu is.
Problem 2: When the toolbar is updated, it fatals on unrouted URLs.
Step 1 creates a link with a URL of base:/admin/foo, which will fatal.
So would http://example.org, which should be valid.
This patch addresses problem 2, but skips over problem 1 by putting a drupal_flush_all_caches() call in the test.
Comment #8
tim.plunkettAt the end of toolbar_toolbar(), it adds in cacheability info to the administration portion of the toolbar. This includes #cache][tags][] = config:system.menu.admin
I'm guessing that should be the entity? Or is that supposed to work?
Comment #9
dawehnerDoes that basically mean the menu tree cache entry should have all of its items as cache tags?
Comment #10
dawehnerOh I see! Yeah there was a point in time, where this was the same, but this is no longer true.
here is a patch with a regression test for the current issue summary. (It needs quite some time, so I kinda assume it will fail)
Comment #12
tim.plunkettAttempt at a better title.
Comment #13
dawehnerThe underlying question, can / should we protect against such mistakes and maybe trigger an ERROR + return an empty string?
Comment #15
tim.plunkettWell an external URL should work fine. I'm not sure what to do about an unrouted one.
Fixing a minor problem in that test, and adding it to my patch.
Comment #17
tim.plunkettI don't understand toolbar caching, menu tree caching, or entity "list cache tags" enough to move this further.
Comment #18
jibranBetter desc please.
Comment #19
dawehnerMeh, we probably need some text here.
Remove this as part of a bugfix or as part of the patch?
Comment #20
tim.plunkett#17 addresses #19. Time-travel!
Comment #22
dawehnerI always knew tim has superpowers! Finally its out!
Comment #23
catchThat could cause us trouble - lots of cache tags to check for invalidations, and the number of menu items in a menu is almost unlimited.
When we change an item in the menu tree, we should be able to invalidate the menu cache tag no?
Comment #24
BerdirI'm pretty sure we used to do that, that's what caused tons of identical cache tag invalidations and lead to adding the static cache there. Not sure if we still do.
Comment #25
BerdirWe definitely still do I think. See MenuTreeStorage.:save(), which is called by MenuLinkManager::add/updateDefinition, which for example is called by MenuLinkContent::postSave().
Comment #26
dawehnerRight, but saving a view also now triggers potentially the menu tree to change, I guess we don't take that yet into account?
Comment #27
Crell CreditAttribution: Crell at Palantir.net commentedLinking to a similar symptom issue.
Comment #28
tim.plunkettSo all of the proper cache tags are invalidated, but the menu tree is still loaded from an old cache.
This is because it's cached as
config:system.menu.
, with no menu name.Both toolbar_prerender_toolbar_administration_tray() and _toolbar_do_get_rendered_subtrees() call $menu_tree->load(NULL, $parameters); for no apparent reason, and that NULL is messing everything up AFAICS.
Changing it to 'admin', which is what it was before MenuLinkNG pt 4, fixes the tests.
The FAIL patch has the new fix without the other toolbar changes.
Comment #30
dawehnerI kinda dislike that toolbar is not written at all in a generic way, seriously, it feels more like a custom module, but sure, coupling it to a specific menu does not make the situtation worse at all, given that its already coupled to a specific menu item.
Do you mind adding a follow up which ensures that you cannot pass in NULL, there? I guess an assertion would be the perfect usecase.
Comment #31
Wim LeersFirst: if a wrong cache tag is the root cause, then let's add a
assertCacheTag()
assertion.It kinda does make it worse, because now you must use the admin menu; in D8 HEAD, you can create your own admin menu and the toolbar module would just use that instead: whichever menu contains the
system.admin
menu link (i.e. thesystem.admin
route, i.e./admin
) that represents the root of the admin menu, would win. At least, that's what I remember from when Jesse Beach worked on this.Isn't the actual problem this:
i.e. the fact that
$link->getMenuName()
returns NULL apparently? But actually… I cannot reproduce that: it actually is'admin'
, without any of the patch applied. So I don't think that's the actual cause?`Comment #32
tim.plunkettThat code is in MenuLinkTree, and it absolutely has the correct $menu_name.
The problem is in \Drupal\Core\Menu\MenuTreeStorage::loadTreeData(), which is called directly from \Drupal\Core\Menu\MenuLinkTree::load(), which toolbar purposefully passes NULL as the menu name.
There is nothing in this method that I can see that would be able to populate that menu name.
So it's not the #cache cache tags that are wrong, it's the ones for the actual cache_menu entry, which AFAIK are not passed to X-Drupal-Cache-Tags.
Comment #33
Wim LeersAhh! Oops, sorry! In that case, +1, but here's another review first:
If we do this, then I think we might as well remove the
setRoot()
call.Then the toolbar is simply tied to the
admin
menu, and not to both a specific menu itemand
a menu.This then opens the door in 8.1 to make it configurable which menu is used.
+1 in principle. But note that
\Drupal\system\Controller\SystemController::overview()
+\Drupal\system\SystemManager::getAdminBlock()
also useMenuLinkTree::load(NULL, …)
! And this dates back to before #2301317: MenuLinkNG part4: Conversion. The idea is to query the entire menu tree I guess. So we should investigate it further before just removing it.Comment #34
tim.plunkettOpened #2535208: Require a menu name in \Drupal\Core\Menu\MenuTreeStorageInterface::loadTreeData() and \Drupal\Core\Menu\MenuLinkTreeInterface::load() to discuss removing support for NULL.
Moving back to RTBC, because this is broken, we have a fix, and the magic "whatever menu contains
system.admin
" feature seems unimportant in comparison, and removing it is not a regression from D7.Comment #35
tim.plunkettCross post.
Comment #36
dawehnerNo question, it should not be part of this issue.
So the remaining bits of this issue is to remote the setRoot() call?
Comment #37
Wim LeersYep, and I've been talking with Tim about that. Patch soon, I think. Then this is RTBC.
Comment #38
tim.plunkettHad to tweak the parameters a bit more than just removing the setRoot call.
This now makes #1869638: Make the menu shown in the administration menu tray configurable a bit easier.
Comment #40
dawehnerI think I won't RTBC that.
Comment #41
tim.plunkettUgh forgot to pull. automatically resolved during rebase.
#2534830: Toolbar subtrees not working and #2534856: Remove unused _toolbar_get_user_cid() touched the same file, no conflicts.
Comment #42
Wim LeersShould be green, manually tested again, RTBC.
Comment #43
dawehner+1 to RTBC
Comment #44
mtiftTim told me to do this.
Comment #45
tim.plunkett@mtift helped me with #28, which was a big breakthrough.
Adding Wim to the proposed commit message as well.
Comment #46
webchickSorry, I went in hoping to commit this, but ended up instead with questions. :(
I do not understand the correlation here at all between what data was mocked and what is being asserted. Why is 'edit-form' pointing to this URL? Why is
<front>
an external URL? Can we explain the thinking here a bit in the comments?It is non-obvious what these magical numbers mean, so it would be great to get a comment above each to explain.
Minor, but both of these are doing the same thing but are written two different ways. We should probably standardize on the route name lookup.
Same feedback here.
Here we do some set up, but don't seem to assert that anything happened. Omission?
In the meantime, saved the recommended commit credit (including Crell, who reported the issue).
Comment #47
tim.plunkett#46
1) That was me being fancy with URLs, I agree it makes it more confusing and harder to read. Reverted.
Also added comments to clarify what we're asserting.
2) Agreed, did my best to document.
3/4) Switched to path-based
5) Without the fix, this would throw an exception and simpletest would register that. But to make it clear, added an assertResponse and comment.
I am boldly setting this back, as there were no functional changes (just adding one assert).
Comment #48
webchickFantastic, thanks!!
Committed and pushed to 8.0.x. W00t!
Comment #50
josephdpurcell CreditAttribution: josephdpurcell commentedI followed the original instructions (which are no longer available) to reproduce this issue and was able to. Rebuild did not resolve it.
I applied patch #47 and it fixed the problem. Nice work!
Comment #51
Codenator CreditAttribution: Codenator as a volunteer commentedComment #53
mtift