Problem/Motivation
#2272801: Allow blocks to be context aware. provided blocks the ability to be context aware.
However, if a context-aware block plugin were written right now, it would appear under admin/structure/blocks, but would not actually be usable since the block system does not set context.
Plugin managers need to be able to restrict certain listings to the contexts that are available.
Additionally, if a context-aware system were actually written, there is no way to actually use those contexts to satisfy a constraint, or to prepare a plugin by setting its expected contexts.
Proposed resolution
Add a ContextHandler class and service, providing the following methods:
- getAvailablePluginDefinitions()
- getValidContexts()
- preparePluginContext()
- checkRequirements()
Add a ContextAwarePluginManagerTrait for helping plugin managers to deal with context-aware plugins
Fix the BlockManager to use these new methods
Comes with unit and integration tests.
Remaining tasks
N/A
User interface changes
N/A
API changes
N/A
Comment | File | Size | Author |
---|---|---|---|
#18 | context-2277981-18.patch | 32.79 KB | tim.plunkett |
Comments
Comment #1
tim.plunkettEclipseGc should also get credit on this commit, since this class is a heavily refactored version of his code.
Comment #2
tim.plunkettOkay, here's a web test showing how the unsatisfied context-aware block should be removed from the block list.
Comment #4
tim.plunkettComment #5
tim.plunkettDiscussed with @eclipsegc and @fago, agreed that this is not ideal for the long run, but we'll be unable to make any progress without this, so we'll try to improve TypedData as we go.
Comment #7
tim.plunkettAdjusting for #2116341: Apply defaults to definition objects.
Comment #8
fagoComment #9
tim.plunkett@fago pointed out that this really should be an array of context definitions. Nowhere in this call stack does it need the Context object or value, just the definition. This means we're blocked on #2281635: \Drupal\Core\Plugin\Context\Context should use a ContextDefinition class instead of arrays.
Comment #10
tim.plunkettActually, #2281635: \Drupal\Core\Plugin\Context\Context should use a ContextDefinition class instead of arrays is further away than I thought, and I think its valid to get this in, and switch later from Context to ContextDefinition when we can.
Comment #11
tim.plunkettgetAvailablePluginDefinitions was a misleading name, since the word "available" does not help convey that it returns a filtered set of plugin definitions.
Since we're passing in an array of contexts to filter, after talking with @msonnabaum, @kimb0, and @fago, we've settled on
getPluginDefinitionsForContexts()
for the service's method.When on the plugin manager itself, it's just
getDefinitionsForContexts()
, to mirrorgetDefinitions()
.Comment #12
tim.plunkettQuick reroll because I left out ConditionManager, and noticed some cruft.
Comment #13
fagoI'm not sure requirement is the right term here as it might contain optional context as well.
What about $contexts (described as available contexts) plus $needed_contexts ? I.e., probably the requirements move to ContextDefinitionInterface in the follow up as well.
Minor, but I'd have expected this helper to be below getPluginDefinitionsForContexts().
It was not clear to me what $definitions need be passed here exactly before reading the code. From the method name I'd have expected it to use $this->getDefinitions() internally. If they are passed trough it's actually not getting you anything, it's filtering.
That wrongly hardcodes a class; i.e. needs at least a todo to use the right class or migrate to context definition.
"Valid context" incorrectly makes me think of context that passes validation. Can we call this "getMatchingContext()"
Does it have to match the plugin context or not? We need to be specific of what kind of context is expected where, i.e. available or needed context or whatever we end up calling them.
Instead of checking for the interface here, can we just pass in the context assignments and let the method apply them?
-> applyContextMapping() would make sense to me.
This should throw an exception if something is in the assignment but it cannot be applied. Silently skipping it even might have security implications, e.g. silently skipping an optional account for which to check access...
Some methods test methods miss a doc block.
ouch
Comment #14
tim.plunkett1) The first thing we do is check
if ($requirement->isRequired())
. So yes, some optional things might be passed in, but the method is concerned with actual required ones, and I think this naming is fine for now.2) Moved, makes sense.
3) Discussed with @fago, this is just filtering, there is no "getting". Renamed to filterPluginDefinitionsByContexts()
4) Add an @todo, same as the others.
5) Agreed, renamed
6) Clarified this
7) Renamed, and passed the assignments in.
8) Added an exception
9) Added.
10) It is what it is :)
Comment #15
fagoThanks, mostly some more minor stuff left:
It's not the array of context to set, it's just the available context - whatever that is :)
$mapping ?
Maybe, improved it to
expects a context named 'node' just to avoid confusing people that this is not entity:node
There should be an interface for that - sry for missing that earlier.
Comment #16
tim.plunkett1) Just dropping the "available" part. It *is* an array of contexts, provided by the calling code. We can't make more assumptions about it.
2) Renamed.
3) Good point
And, added an interface.
Comment #17
fagoThanks, changes look good. -> RTBC.
Fond one minor remark, but that should not hold us back from committing this.
array of matching contexts
Comment #18
tim.plunkettRerolled with that change, just to keep things easy. Thanks @fago, this code is much better than what we started with!
Comment #19
tim.plunkettEclipseGc and fago should also get credit for this.
We started with https://gist.github.com/EclipseGc/f0cd16407d09e2362314, which was essentially copied straight from D7 CTools, and it has been refactored and polished since then.
Comment #20
alexpottCommitted 47e7496 and pushed to 8.x. Thanks!
I think the
$this->assertTrue(empty($elements), 'The context-aware test block does not appear.');
is more than a little bit brittle but we can fix that when it bites us back.Comment #22
tim.plunkettThanks!