Problem/Motivation
This is split off from #2352155: Remove HtmlFragment/HtmlPage and is a blocker for that issue.
SystemMainBlock
in HEAD calls drupal_set_page_content()
to get the main content it should display. This is the case for historical/legacy reasons, but we need to fix it if we want:
- to make the (HTML page) rendering pipeline understandable at all
- to make SCOTCH possible, i.e. to have page display variants work sanely (introduced in #2286357: Introduce Display Variants, use for the block rendering flow)
- remove the incredibly crufty behavior in
drupal_set_page_content()
that's been around for so long
Proposed resolution
Display variants are a generic, abstract concept: they're intended to represent different variations of displaying things. For displaying variations of pages, which always have some "main content", it needs to be possible to somehow send that main content to the variant.
In HEAD, that happens through a global static: drupal_set_page_content()
. We don't want that to continue (especially because this was nigh impossible to understand), and therefore we want to be able to send the main content to a variant directly. For that purpose, I introduced \Drupal\Core\Display\PageVariantInterface
, which subclasses the generic variant interface. Its sole addition: a setMainContent()
method.
Now the code invoking a page display variant can send the main content and let the variant deal with it further!
In HEAD, we already have the SystemMainBlock
, which is rendering the main content. Until now, that was using drupal_set_page_content()
, i.e. that global static again. The Block module's page display variant already receives the main content cleanly (as per the above), so now we need to pass it cleanly to the block(s) rendering the main content as well. For that, a similar interface was introduced: \Drupal\Core\Block\MainContentBlockPluginInterface
, a subclass of BlockPluginInterface
, with again a sole addition: a setMainContent()
method.
So now the code responsible for rendering a page variant (in #2352155: Remove HtmlFragment/HtmlPage, we'll introduce a proper way to select a page variant, but for now, we continue to use HEAD's practice of a hard-coded page variant) can always call setMainContent()
on the page display variant (since page display variants must implement PageVariantInterface
), which then in turn has the responsibility of rendering the main content, and in the case of blocks, we detect which block renders the main content by checking if it implements MainContentBlockPluginInterface
, and if it does, we again call setMainContent()
. Et voilà!
Remaining tasks
Review.
User interface changes
None.
API changes
Comment | File | Size | Author |
---|---|---|---|
#15 | almost_rm_drupal_set_page_content-2363025-15.patch | 14 KB | Wim Leers |
Comments
Comment #1
Wim LeersComment #2
tim.plunkettFullPage still exists, Simple and Demo do not.
Not used
Comment #3
dawehnerThis itself just makes sense and is kinda exactly what we planned earlier with MainHtmlFragment.
Maybe micro-opt but we could store the result of $block->getPlugin()
... A little bit more help would be nice!
Comment #4
znerol CreditAttribution: znerol commentedI'm not comfortable with that. What if we pass in the block id of the main block via plugin configuration? Something like this (obviously the config also could live in a yaml).
Comment #5
Wim LeersD'oh; this was extracted 1:1 from the main patch; will fix both those points — thanks!
Comment #7
catchComment #8
Wim LeersAs of the above patch, we always call
drupal_set_page_content()
when block module is enabled. Even if we don't actually render the main content. This means thatdrupal_prepare_page()
will assume we've rendered the main content, and not do this:But, most tests (all of the failing ones) DON'T place
SystemMainBlock
. Hence the main content doesn't become visible.So the question is: how do we make sure that
FullPageVariant
always displays the main content? The answer: we check if we rendered a block that does show the main content, and if we didn't, we show it manually. Much likedrupal_prepare_page()
does.The only alternative is to let each of those failing tests place
SystemMainBlock
… which is a huge amount of work.This moves the responsibility of rendering the main content from the mess/maze/legacy cruft that is
drupal_prepare_page()
(which relies on crazy hacks with static variables) into clear logic. Other page variants won't have to perform this mess.Note: this is in fact exactly what the main patch does; see the same files as the ones in this interdiff in the main patch.
Comment #9
tim.plunkettThis is the same problem we had in #2295363: Blocks should be added to the page via an HtmlFragmentRenderer, the main content fallback.
Comment #10
Wim Leers#2: fixed in #8!
#3:
#4:
Why aren't you comfortable with that? What's problematic about it? In theory there could be multiple "main content" blocks. Your proposal requires us 1) to know which is the main block, 2) doesn't allow multiple main blocks, 3) doesn't allow an alternative implementation of the main block.
I don't see any benefit to what you propose, and no downside to what's in the current patch? Can you please explain a bit more what you find so problematic? :)
Comment #12
znerol CreditAttribution: znerol commentedRe #10
This seems to contradict the concept of "main content" (the main thing).
It does not have to be hard-coded at all, the configuration could easily live in a yaml.
This part just looks weird to me. If the
FullPageVariant
knew which block handles the main content, it simply could forward thesetMainContent()
call to that block. The$mainContent
instance variable could be removed and the loop inbuild()
could be left as is.The rest of the patch looks fantastic. Also I do not want to hold this back, my concerns are mere based on a gut feeling.
Comment #13
larowlanFixes fails.
Reviewed this and can't find any issues.
Comment #14
dawehnerIs there a reason why we not just simply set the main content of the $page render array? so _block_page_build() can already take it from there? Well, no teal.
Tried to improve some small bits.
Comment #15
Wim Leers#13: Thanks! But we should test both with and without
SystemMainBlock
being placed. The main patch provides test coverage for that, so cherry-picking those hunks out of there into this patch also.Also addressed #3.1, #3.2
#12: But then how is the system supposed to figure out which block is "the main content block"? You'd end up with something similar to this anyway, to figure that out automatically, unless you want to burden the site builder with that effort: (s)he'd have to indicate which block should contain the main content in the UI, which would be a UX regression and very brittle.
This is probably the first time I'm disagreeing with you, so I definitely want to understand your concern and understand how you'd like to see it work!
Comment #16
Wim LeersIs there a reason why we not just simply set the main content of the $page render array? so _block_page_build() can already take it from there? Well, no teal.
We could do that now, I think, but the main patch will remove that: even when block module is disabled, we'll use a page display variant, so that we have a consistent rendering pipeline, that always uses a page display variant. In that patch, that default page display variant (which is used when Block module is not installed) is called
SimplePageVariant
(name can be changed, of course), and then the page render pipeline looks like this:When Block module is enabled, it just always overrides it to its own page display variant. If Page Manager or Panels or something else is enabled, then that would be used. When using multiple, it'd be up to the event priority and whatever logic the event subscriber uses to determine which wins.
I hope that made sense.
Comment #17
Wim LeersAny other concerns, or is this RTBC? :)
Comment #18
moshe weitzman CreditAttribution: moshe weitzman commentedDon't see any outstanding objections.
Comment #19
Fabianx CreditAttribution: Fabianx commentedRTBC + 1 - Great work!
Comment #20
alexpottCommitted ffe035e and pushed to 8.0.x. Thanks!