Problem/Motivation
Currently, the PageDisplayVariantSelectionEvent allows you to set which plugin id should be used as the DisplayVariant to render the current page.
Core only provides two variants:
- Drupal\Core\Render\Plugin\DisplayVariant\SimplePageVariant
- Drupal\block\Plugin\DisplayVariant\BlockPageVariant
Neither of them need to take configuration - SimplePageVariant is basically a no-op, and BlockPageVariant uses essentially global data.
However, VariantInterface
extends ConfigurablePluginInterface
, so it allows for plugins to take configuration, and any variant that works with Page Manager needs to take configuration in order to work!
So, to allow Page Manager variants to be use by core (in order to implement something like Panels Everywhere), we'd need to also set configuration on PageDisplayVariantSelectionEvent too.
Proposed resolution
I propose adding setPluginConfiguration(array $configuration)
and getPluginConfiguration()
methods to PageDisplayVariantSelectionEvent, which would be used by Drupal\Core\Render\MainContent\HtmlRenderer::prepare()
when instantiating the variant plugin.
Remaining tasks
Write the patchWrite PoC of Panels Everywhere demonstrating how this would be used- Get reviews
- Commit!
User interface changes
None.
API changes
Adding a getter and setter to PageDisplayVariantSelectionEvent
.
This won't affect core because none of the variants take configuration.
However, this will allow us to write Panels Everywhere for Drupal 8!
Data model changes
None.
Beta phase evaluation
Issue category | Bug because Variants can take configuration (they extend ConfigurablePluginInterface) and some variants (like the Panels and Page manager variants) requires it. But the PageDisplayVariantSelectionEvent class in core doesn't provide a way to pass that configuration, making it impossible to fulfill the Variants API. I suspect most variants provided by contrib will take configuration! |
---|---|
Issue priority | Major because it renders this event useless for any Variant that needs configuration, which will likely be any Variant provided by contrib. The core variants are kind of exceptional in NOT needing configuration. |
Disruption | Disruptive for core and contrib will be minimal because this only adds a new getter/setter, it doesn't change any existing method signatures. |
Comment | File | Size | Author |
---|---|---|---|
#25 | 2512062-25.patch | 8.43 KB | tim.plunkett |
#25 | interdiff.txt | 1.42 KB | tim.plunkett |
Comments
Comment #1
dsnopekIssue summary update and better (ie. shorter) title.
Comment #2
dsnopekAdded writing a PoC of Panels Everywhere to the task list.
Comment #3
dsnopekHere's my first pass at a patch!
Comment #4
dsnopekComment #5
dsnopekI didn't add an argument to the constructor for the plugin configuration to minimize API change. The current patch only adds the getter/setter and doesn't change existing APIs. But I don't know if that actually makes sense. :-)
So, I'd love some feedback on that in particular!
It's likely only core that creates this event, so it'd probably be safe to change the constuctor's signature.
Comment #6
dsnopekHere's a proof-of-concept implementation of Panels Everywhere, which includes the config for a Page Manager Page, and an event subscriber listening on
RenderEvents::SELECT_PAGE_DISPLAY_VARIANT
which returns the selected variant from that page:https://github.com/dsnopek/panels_everywhere_poc
It won't work with the current 8.x-3.x-dev of Panels, because we still need to solve #2511570: Remove Drupal\page_manager\Plugin\PageAwareVariantInterface, but as a work around to see it working now, you can comment out the foreach loop in
PanelsDisplayVariant::getContextAsTokenData()
.This also includes its own display variant plugin, but this really shouldn't be necessary -- PanelsDisplayVariant should implement PageVariantInterface and do the extra work to support that. No issue created for that yet.
In any case, the most important bit is this code in the event subscriber:
In this file: https://github.com/dsnopek/panels_everywhere_poc/blob/master/src/EventSu...
It shows how it's selecting the variant from the Page Manager Page. Theoretically, there could be several variants with different selection conditions. When it finds the right one, it gives the event the plugin id and the configuration, and HtmlRenderer will use it to render the page!
Comment #7
andypostthe main question here why the event needs this method because page variant is instantiated not here so event just selects plugin then it should get config in some other place
and minor nits
if ($vc = $event->get..
should be enough, better not spread get and usage in 30 lines
protected $pluginConfiguration = [];
Comment #8
dsnopek@andypost: Thanks for the review! Here's a new patch. :-)
I also added the beta evaluation to the issue summary! I'd appreciate feedback on that. I ended up changing the priority to Major, since that seemed to apply more than "prioritized change."
However, I'm not sure about the tests. Do we really need to test a simple getter and setter added to the event object? And the change to to HtmlRenderer is just passing the data from one getter to another setter. The amount of code required to test that would be large, and I think the value would be minimal. But if necessary I can add some tests!
Thanks again!
Comment #9
dsnopekEr, sorry, I messed up rolling that patch. Here's a better one! The interdiff on #8 is correct, though.
Comment #10
dsnopekOh, jeez, my Git-fu is failing me this morning! Not sure how I could get the interdiff right, and the patch so wrong, but THIS one should be right. Sorry for all the extra comments.
Intediff on #8 still correct.
Comment #11
dsnopekAbout adding tests:
I don't think it makes sense to unit test the getter and setter added to
PageDisplayVariantSelectionEvent
, since we aren't testing that getters and setters work anywhere else in core (at least that I could find). I could see adding tests for the change toHtmlRenderer::prepare()
, however, there aren't currently any unit tests forHtmlRenderer
at all and it's calling\Drupal::theme()
directly, so at the moment it can't be tested. I created a new issue for that: #2535212: Add test coverage for HtmlRenderer@tim.plunkett suggested creating a web test (rather than a unit test) that provides a variant that needs configuration, and makes sure that it was passed when the page was rendered. So, I'll take a stab at that next!
Comment #12
dsnopekAlright! Here's a new patch with tests like the ones that @tim.plunkett recommended.
Comment #13
dsnopekAs recommended by @tim.plunkett, here's a FAIL patch that has the change to
HtmlRenderer::prepare()
removed to show that the tests fail. And I've attached a PASS patch too, which is identical to the patch in #12, but hopefully it'll reset the testbot to passing.Comment #15
tim.plunkettEven though this won't run (except in the test-only), it should be
throw new \Exception
(missing new)Comment #16
dsnopekOops! Thanks, I've fixed that. :-)
Comment #17
tim.plunkettLooks great, thanks!
Comment #18
Wim LeersWhy was this not necessary so far in the D8 port of Page Manager? Because it was only a partial port?
The test coverage tests the case of
$variant_config
not being empty. Shouldn't we test the other case as well?Or… can this be simplified to:
?
These are generic names. IDK what the convention is, but given that this event only applies to a specific plugin type, shouldn't we be more specific here?
i.e.
(get|set)PageDisplayVariantConfiguration
?Comment #19
dsnopek@Wim Leers: Thanks for the review! :-)
Page Manager (in both D7 and D8) allows you to take over the main content of the page. It's the Panels Everywhere module that allows you to take a Page Manager "Page" and use it to replace the whole page.
I think this is the first time that someone has tried to implement Panels Everywhere. With the change on this issue, it actually becomes quite elegant (as opposed to a hack in D7).
Yes it can! My thinking was just to avoid calling the setter if there was nothing to set. But here is a new patch that does it that way.
Well,
(get|set)PluginConfiguration()
matches the existing(get|set)PluginId()
method names. You end up using it like:So, if we give
setPluginConfiguration()
a more specific name, then to match, we really should givesetPluginId()
a more specific name. However, then this is an API change!In the interest of not making an API change, but only an API addition, I think we should leave this alone. But I don't really care about the names and would be happy to go with whatever the consensus is!
Comment #20
Wim LeersIf we're gonna do it this way, then let's chain it.
Should return
$this
. Then you will actually be able to chain it.Trailing semi-colon that doesn't belong there :)
Comment #21
dsnopek@Wim: Thanks again for the review!
#20.1: Done!
However, I'm not sure on the coding standards for doing chaining. Should both the setters be on their own line? For example:
I went the other way in the patch, but I can switch it?
#20.2: Done! Changed
setPluginId()
to return $this as well. Hopefully, changing the return value from NULL to $this qualifies as an API addition, rather than an API change?#20.3: Done!
So, this patch has gotten a little messier - the one from #16 changed fewer things, but hopefully the changes are still minor enough to make this easy to get in. :-)
Comment #25
tim.plunkettSimplePageVariant::setMainContent violated its interface.
Also, I think we generally do chaining each on its own line.
Comment #26
Wim LeersAlright, back to RTBC!
Comment #27
Wim LeersFor realz.
Comment #28
dsnopekThanks, Tim!
Also, hiding old patches.
Comment #29
EclipseGc CreditAttribution: EclipseGc commentedFWIW I'm about +1000000000 to this patch.
Eclipse
Comment #30
alexpottCommitted 53f6c5a and pushed to 8.0.x. Thanks!
Thanks for adding the beta evaluation to the issue summary.
Unused use removed on commit.
Comment #31
alexpottComment #33
dsnopekWoohoo! Thanks! :-)