Follow Ups:
#2378583: Core ContextAware Plugins have inconsistent ContextDefinition return docs
Per 162.2
#2377757: Expose Block Context mapping in the UI
Blocks should make use of the architecture we introduced here. It should be fairly minor and is the last big move of 8.0.x towards a realistic scotch future.
#2378585: Multiple context requirements cannot be satisfied by a single value
Discovered in tandem between 2377757 and this issue. Our ContextHandler is mapping things sub-optimally and could result in data loss.
Problem/Motivation
Block visibility leverages conditions & their contexts in such a way that equates essentially to a magic naming scheme. This is problematic for conditions which might operate against multiple possible contexts such as the current language condition which can use any available language type (content or interface in core). This is hypothetically true of any contextually aware condition, and the UI & execution of these visibility rules should be altered to reflect that.
This is further complicated by the fact that conditions have been altered in such a way that they are making a lot of assumptions that are specific to core's block approach. This needs to be refactored out so that these are cleanly separated. Currently, all available condition forms are rendered within the block visibility (except current them which is manually removed). Conditions also set supposedly "sane" default on instantiation, and these two features of the system conspire together to make it impossible to tell programmatically whether or not a condition has actually be interacted with during configuration. This means values for the conditions (all of them) will be saved to the block config entity, even if you have no desire to control visibility of this block. That will result in unnecessary processing time for actually determining if a block is currently accessible/visible to the end user.
Proposed resolution
Clean up conditions so that we don't have unnecessary form values passed to us at all times and move them out of block plugins. This may require extra code that attempts to determine if a condition has been set at all, and will certainly require the "sane" defaults portion of instantiating a condition be removed. Tests should be written to cover language's specific functionality needs since that surfaced this problem, and then generic UI tests for multiple context availability should also be added.
What the patch changes
- "Visibility" is no longer part of block plugins directly. This cleanly separates how these two separate systems work and allows some other entity (block in this case) to orchestrate how they work together.
- The ConditionPluginCollection was also removed as it's a completely unnecessary abstraction that complicated the code dramatically and didn't provide any real benefit since there's no need for lazily instantiating Conditions during block visibility or configuration.
- Ultimately, direct control over block visibility has moved to the Block entity. This has big, positive ramifications for contrib modules like Panels and Display Suite that leverage blocks completely differently than Drupal core and allows them a greater degree of control over their own implementations.
- A new event was added that allows event subscribers to provide contexts available during administration since not all context will always be available (for example the node from URL).
- A form method was added to context aware plugins that allows them to get select which context to configure a plugin with if multiple are available. If there is only one context that is useful available to the plugin, the form will automatically map that context as being the one to use w/o any user interaction.
- Context gathering is moved up into FullPageVariant which will prevent it from happening for every single block individually and instead allows for this to be done once and passed to all blocks/conditions from there. @see [#]
- Finally, this patch attempts to determine whether or not a condition actually has legitimate values from user interaction and remove values for plugins that were not configured. This allows us to know which conditions we should actually be asking about this block's visibility.
Patch impact
Despite the size of this patch, it mostly moves existing functionality around to make things saner to use. That's not its sole purpose, but that's the primary feature of the patch. For module developers who are writing conditions or Blocks, this should have virtually no impact. Block plugins, specifically, were holding the visibility methods previously, but that was completely unnecessary and the default functionality of those methods was the expected standard for all blocks specific to core's use case. This patch touches no single block plugin and only changes the base by removing methods.
Conditions had one change of note which was the removal of a method call during __construct() that set defaults for all the form elements. This should never have been done in the first place and made it difficult to tell whether a user had interacted with the condition or not. Ultimately the primary impact of this change is that condition plugin developers will need to check their form defaults differently. Worst case scenario, this results in some minor warnings about missing array keys for unconfigured conditions.
The positive side to all of this is that Panels & DS-like solutions should be able to leverage blocks directly and conditions directly for visibility if desired. This will allow them to build UIs that make sense to them w/o having to untangle a lot of core assumptions. This should also make our own UI improvements in this area easier in the future.
Remaining tasks
Get someone to RTBC it.
Get it committed.
Related tasks
Lays the groundwork for a super simple #2349679: Support registration of global context
Seriously reconsider doing #2284687: Redesign UI for better management of block visibility
Remove the need for #2354889: Make block context faster by removing onBlock event and replace it with loading from a ContextManager
#2362727: Implement __toString() on Translation Annotation
User interface changes
none
API changes
Currently all the block visibility methods exist as part of the BlockPluginInterface. This is simply not acceptable and we need to move these to the BlockInterface instead. Conditions are likely to need some cleanup as well including changing things they do during __construct(). I'm still dealing with the fallout of the various changes that have been made to conditions, but in general we should make sure they are not so tightly coupled to core's block visibility use case.
These API changes are unlikely to affect any developer actively creating Block plugins, so despite the change here, odds of a developer actually running into this as a problem are very minimal.
Comment | File | Size | Author |
---|---|---|---|
#169 | 159-165-interdiff.txt | 8.55 KB | alexpott |
#165 | 2339151-interdiff.txt | 2.5 KB | EclipseGc |
#165 | 2339151-165.patch | 122.13 KB | EclipseGc |
#163 | 2339151-interdiff.txt | 4.68 KB | EclipseGc |
#163 | 2339151-163.patch | 121.48 KB | EclipseGc |
Comments
Comment #1
Gábor HojtsyInitial patch. Restores the selection form IF there are multiple configurable language types (code identical to the one removed in #2278541: Refactor block visibility to use condition plugins). Added tests to ensure the type is saved. I could not track down how the context gets set, but we need to modify contexts to have each language so we can rely on it to fix this regression.
Comment #2
Gábor HojtsyHere is an attempt to make the condition work but it does not work yet. The problem is I cannot actually create a new ContextDefinition with a language type name because it is not a valid typed data type. In CurrentLanguageContext::determineBlockContext(). So I create a new 'language' type context and then register it under the name of the language type BUT that does not make it available as such in the actual condition's contexts. (That still has a language context which is not properly filled with value because this code does not set that general one anymore obviously).
So I thought adding a typed data type for each language type (yay?). Since they are dynamic, we need to dynamically add them. The only way seems like for that is hook_data_type_info_alter(), which I implemented in language.module but that did not help either. The language data type itself is at \Drupal\Core\TypedData\Plugin\DataType\Language.
Comment #4
andypostComment #5
EclipseGc CreditAttribution: EclipseGc commentedThis issue hits on a bunch of things that are currently wrong with our approach to contexts/blocks and conditions. I could attempt to solve them all, but I'm just trying to lay groundwork here. I've not updated tests, I want to see what fails and take it from there. I'm also going to make a whole bunch of comments on my own patch after I post it, so here we go.
Eclipse
Comment #6
EclipseGc CreditAttribution: EclipseGc commentedCurrently calling to the BlockEvents class in block module, so we need to fix this, however I really do think that the BlockEvents class belongs to blocks module, so we need to properly untangle this which probably means this needs to be in the entity, not the plugin. That's just a hunch.
Most of the special things about this PluginBag need an interface & trait so that others (BlockPluginBag for instance) can make use of them. It's important to keep this sort of logic at the PluginBag level because it makes it possible for different implementations to solve context requirements in different ways.
meh. It works.
Must be missing a test somewhere because the intent was for this to always return a string. Going to have to investigate further.
This abstracts the logic for getting context form components and context configuration into a common plugin class. With the exception of the custom stuff in the ConditionPluginBag, blocks could be context aware (including form elements) today. That's pretty exciting.
ok, so this is actually run-time and config-time context availability. There's a big big difference. For instance:
You'll never have a node available at config time because you're not on a node route. You have to have different events for this.
Most Context providing event subscribers will just call their determineBlockContext() method, but as cited above, nodes have to be done by hand. This is super cool though because it means that an new subscriber could be introduce that exposes nodes in some other way, and the form elements for that would just be autogenerated and the contexts appropriately passed. YAY!
Hopefully this self-review explains what I did and why. I'd like to take this further, but it seems like it should be follow-up territory.
Eclipse
Comment #7
EclipseGc CreditAttribution: EclipseGc commentedI bet this is going to blow up, I probably needed to include some config updates. Let's see what happens though.
Eclipse
Comment #9
EclipseGc CreditAttribution: EclipseGc commentedOk, I _THINK_ this should pass tests. I made a couple changes that are questionable here, primarily the schema change for condition plugins. Tim Plunkett rightly points out that it may already be in use under the old name, so if we decide to change it, we'll just need to update the code to match the old name.
The other thing here is that the ConditionFormTest is probably going to need some additional tests to cover various context availability. I've not even attempted to consider that yet. Likewise we need to double check all the Condition tests, but I'm just trying to get the stuff that absolutely failed previously passing and then I'll make another iteration on these things.
Eclipse
Comment #10
EclipseGc CreditAttribution: EclipseGc commentedoops
Comment #11
Gábor HojtsyThis is so great! I don't have architectural feedback, I don't think I would be a good person to validate this architecture. I would suggest the usual suspects, but you are in discussion with Tim Plunkett already on this. I don't have better suggestions :)
Not clear to me why this is moved here?
Not clear to me. Why would you want to return an empty context to properly configure? I know why you would want to return multiples, but an example (languages) would be good.
Types that don't have a name should not be included. See \Drupal\views\Plugin\views\PluginBase::listLanguages()
The test should actually test that the types are configurable and how it works with separate config.
Comment #12
EclipseGc CreditAttribution: EclipseGc commentedAgreed on points 3 & 4.
Point 1.)
The reason this was moved is because core has a very specific (and not particularly flexible) methodology for managing available contexts. We couldn't embed this as part of the block itself because we don't want to inherit that implementation. This allows us to cleanly separate the assumptions of core from the desired reusability of both blocks and conditions by embedding these assumptions in a core module instead of porting this rather limited approach to contexts into a Component.
Point 2.)
Empty contexts are for use during administration. Nodes are a great example of this since we won't ever have a node available from the URL when on the blocks UI, but we know one can be available, so we provide an empty one during administration time so that we can properly configure the plugin. (The system programmatically configures these when only one is available) In the case of multiple contexts, obviously language is a great example. If we want to test block visibility against Content Language or Interface language, we need to pick which we want to test. Since there are certain scenarios where these are NOT always the same, you may find that custom administrative blocks are useful to your foreign language content administrators and this is very different from the use case of swapping blocks based on content language.
Hopefully that is fairly logical and explains the need for these things appropriate.
Eclipse
Comment #13
EclipseGc CreditAttribution: EclipseGc commentedOh, on point 4... This is completely unnecessary any longer (the specific code) now we need to pass a language to the plugin as context so that it can properly test. This may require us to set the context in the configuration to get proper coverage, but I didn't want to dig into this yet. The previous patch is just trying to get the existing tests to pass, we should probably make up a batch of tests that hit our coverage properly and do the whole FAIL/PASS dual patch thing. I'll try to take care of that before Amsterdam, but a lot of stuff happening right now.
Eclipse
Comment #14
tim.plunkett\Drupal\Core\Plugin\Context\ContextHandler uses context_mapping
We don't use #-prefixed properties in $form_state
If you're going to move this back up a level, it has to actually go up a level. The block.module form doesn't get to touch 'settings', that needs to be completely controlled by the plugin
We absolutely need this second event, thanks
Comment #15
EclipseGc CreditAttribution: EclipseGc commented3.) BAAAAAAH yuck you're right. Ok, I'll see what I can do.
Eclipse
Comment #16
Gábor Hojtsy@EclipseGC re #12/2 these would be great to document in the documentation itself. It may be that the empty contexts are documented elsewhere and it is just me not having enough of an overview.
Comment #17
EclipseGc CreditAttribution: EclipseGc commentedI did document it right here (and also a specific implementation on the node context subscriber)
FWIW, chx has asked to help document the ContextInterface as well, #2344045: ContextInterface needs documentation and I wrote up https://etherpad.mozilla.org/50vwJOqZEO as something to bounce back and forth with him last night. This should help in this regard as well.
Eclipse
Comment #18
Gábor HojtsyRight, I seen that doc and quoted it in my question in #11/2. Your issue comment above *actually* explained what empty and multiple contexts would be for, while this quoted code comment does not explain that, that is what I suggested to bring over to the code docs.
Comment #19
EclipseGc CreditAttribution: EclipseGc commentedOk, this was a bit of effort. Moving visibility up out of settings made it kind of obvious that the block interface visibility stuff had to move to the entity as well. Updates as relevant.
Context mapping was changed back to what the ContextHandler expects, however since this issue has grown into a more generic context handling solution, I changed block's schema to match since it was still "contexts".
Other then that I've tried to take all the feedback here into account except for Gabor's issue with my docs. The patch was working and burning a whole in my pocket. I'll check to see tests tomorrow and if all is well, proceed with further docs and tests.
Eclipse
Comment #20
EclipseGc CreditAttribution: EclipseGc commentedhotel wifi--
Comment #22
tim.plunkettJust to get more info out of the test failures. The BlockBaseTest I commented out needs a new home.
Comment #24
fagoI reviewed the patch. The general approach makes sense to me, while I'd like to see global context to be separated into its own event. But as discussed, that can be a non-API breaking follow-up issue.
Here some remarks:
Missing NL between the two lines.
Documentation misses description.
That's funky, is there no better way to trigger validation? Same for submit.
That's a bit weird, cannot it be just either of both?
Shouldn't it require configuration as well? I see there is a setter, but why not have it as second parameter to make sure it's there?
What are defined contexts for a condition? Does it mean context available to the block?
Comment #25
EclipseGc CreditAttribution: EclipseGc commented1.) Noted
2.) Noted
3.) It's how we've done it for forever. I'm not opposed to discussing changes here, but probably follow-up material. Also not sure what you have in mind.
4.) ConditionPluginBag is iterable, so both are simultaneously true.
5.) No, I think all of that is provided to the plugin bag during instantiation, and we're just asking it for the specific instance that it already has all that information for.
6.) No, the context is actually not (yet) available to the block. It's just contexts available for the visibility conditions.
Eclipse
Comment #26
fagoad 3, I see - its own issue then.
>4.) ConditionPluginBag is iterable, so both are simultaneously true.
Yes, but it's weird not to know what you are iterating on. It should be clear what kind of animals this method returns.
>ad 5)
I see.
> 6.) No, the context is actually not (yet) available to the block. It's just contexts available for the visibility conditions.
ok, so the docs could use some clarification here as it was not clear to me what context this is about. available, required context?
Comment #27
fagoComment #28
Gábor HojtsyIt would be useful to discuss this with a core maintainer or two if they are ok with the extent of the change. I *think* this is not considered a major subsystem that should only change for critical issues, but I am *not* an authority on this. This is great but if you burn yourself on this and it would not be committed, that would not be great!
Comment #29
fagoAdded an issue for separating global context #2349679: Support registration of global context.
Comment #30
EclipseGc CreditAttribution: EclipseGc commentedGabor,
I've spoken with Angie. No one can give me any guarantees obviously, but I'm hoping that since the API that changes here is used by a very small subset of contrib modules and it is largely moving methods around (and fixing tests to match) that we'll be ok.
What is complicating things right now is that our old visibility UI displays form elements no matter what. With the addition of context to the system, this gets tricky because context is always saved, event when you set no visibility rules. This can result in blocks that disappear, even though their visibility settings haven't been altered. The simplest fix to this is to re-work the UI to only show forms for visibility conditions we actually want to use. (i.e. the user opted into them) The other option is building some system that can know if the configuration for the conditions is "null" and have it throw away all the null values (including context which is not null). We don't have a system like that so I'm sort of debating my way forward here. I ran into this today and have not come to a conclusion.
Eclipse
Comment #31
Wim LeersI reviewed this from a high level.
This name doesn't make sense. Cache contexts should remain unchanged.
This change looks like it *could* be a performance regression.
Applying block visibility is very cheap. Running the access system, plus the things it needs to load in order to perform access checking, is much more expensive.
But, having seen the changes in
BlockAccessControlHandler::access()
further down the patch, it looks like this execution order is still the same, hence it's fine :)Event dispatching doesn't happen very often. We should document this very, very clearly.
(Also: the service should be injected.)
Comment #32
EclipseGc CreditAttribution: EclipseGc commentedre-point 1.) I totally agree. I was moving too fast and didn't realize this was cache context. I found it during testing and just having uploaded a new patch since I was struggling with the issues I mentioned in #30.
Eclipse
Comment #33
EclipseGc CreditAttribution: EclipseGc commentedThis patch won't work, but at least it documents what I've been doing.
Eclipse
Comment #34
Gábor HojtsyElevating to major, retitling for the more general case (fix if you have a better idea).
Comment #35
EclipseGc CreditAttribution: EclipseGc commentedGoing to rework the summary today. Here's a current patch, it still won't pass but I want to see how badly the tests are doing after this last set of changes.
Comment #37
EclipseGc CreditAttribution: EclipseGc commentedComment #38
EclipseGc CreditAttribution: EclipseGc commentedrerolled against head.
Comment #40
Wim LeersGlad you fixed it, but it's important that we take cache context into account while working on all this: please reply to #2349679: Support registration of global context.
Comment #41
EclipseGc CreditAttribution: EclipseGc commentedI'm not sure it's important to fix that in this issue. I'd rather keep status quo and not add yet another moving part in this patch. I'm all for the notion of moving current user and current language(s) to a global context solution, but it'll be one more thing to justify in this patch, and I'd prefer to not complicate this further.
Eclipse
Comment #42
Gábor HojtsyI agree there are several moving parts in this code already.
Comment #43
EclipseGc CreditAttribution: EclipseGc commentedComment #44
andypostComment #45
EclipseGc CreditAttribution: EclipseGc commentedOk, I had to resort to some pretty huge changes in order to get some of the basic functionality the way it should be. The ConditionPluginBag was way too fiddly for what I was doing to the condition values, so I ejected it in favor of (mostly) properly injected ConditionManager calls to instantiate the Conditions when necessary. This is a TON easier to read and simplifies the BlockInterface further.
In addition to these changes, I moved context gathering outside the scope of the block completely and moved it into the FullPageVariant. This is a FAR saner solution and allows our blocks to actually leverage context properly when that day comes but also I added methods to the BlockInterface for containing the entire context stack which allows us to pass it to condition properly. The event constants & classes that did this work have been updated to reflect these changes.
I've not run a full suite of tests on this yet, and there are definitely still problems but I wanted a better picture of what these changes may have broken.
Eclipse
Comment #47
EclipseGc CreditAttribution: EclipseGc commentedThis is HUGELY concerning to me. If all configurable plugins are abusing their defaults this way we have a potential problem. It's complicated by block visibility since it's not "opt-in" right now, you just get ALL conditions, but if any other system in Drupal core uses this same approach, it's a huge pain to untangle this and you can't tell which plugins have been interacted with by the user since they're all on screen and all have values. This is actually why I supported defaults in the annotation (ages ago now) because there was no way to confuse defaults with user configuration until the form was submitted. This approach configures a plugin before anything has even happened. Very concerning.
oops...
also oops...
more oops...
We get context here now instead of foreach block's conditions. This means that Contextual blocks are technically possible and that means we could actually filter available blocks by context on the block admin UI. Cool!
need to put a try catch on this that prevents contextual blocks blowing the system up.
This is actually an unrelated bug I ran into when I was first investigating this issue. @Gabor, care to move this into another issue? Let me know.
Need to inject this properly.
I've taken care of the few oopses I had here already locally and they'll be in the next patch.
Eclipse
Comment #48
dsnopekI spent some time reviewing and testing this patch. I think I understand what's being done here conceptually, and if so, I'm all for it. :-) But I'm not an expert in the code this is changing, so I don't really have any valuable line-by-line review. Instead, I'm going to attempt to describe what I think this patch is doing and say why I think this is a good thing:
Anyway, this all seems great! It moves some nastiness from the core API, which many modules will be using, and isolates it in the block module. This will also make our efforts at porting Panels to D8 easier, and will probably have similarly positive effects on other layout modules.
However, my manual testing of the patch in #45 showed some problems. I configured the "Powered by Drupal" block to only show up on "<front>", but it showed up on all pages anyway. It seems like my configuration isn't actually saving?
In any case, great work so far! @EclipseGC++
Comment #49
EclipseGc CreditAttribution: EclipseGc commentedHmm, I'll check the pages one out. Node type and user role are definitely saving.
Comment #50
Bojhan CreditAttribution: Bojhan commentedLets get some feedback.
Comment #51
EclipseGc CreditAttribution: EclipseGc commentedI wasn't really ready to do that, but ok, for committers it's worth noting no block plugins were altered in order to make these changes work. Shouldn't affect anyone implementing blocks, this affects (positively) contrib that wants to make alternate use-cases for blocks.
Eclipse
Comment #52
EclipseGc CreditAttribution: EclipseGc commentedOk, I tried to take care of most of my own commentary from 47 and I removed point 7 on that list as it's not actually related to this. Interdiff is from before I merged to HEAD, PluginBags were named to collections and that complicated this merge somewhat.
Eclipse
Comment #54
EclipseGc CreditAttribution: EclipseGc commentedThe serialization error is weird and I've not tracked it down yet, but thought I'd put up my progress. This last set of tests found an awesome bug I'd missed so ++ to that
Eclipse
Comment #55
EclipseGc CreditAttribution: EclipseGc commentedSince the block visibility is no longer a core part of the block plugin, we no longer need this sort of code in tests (or elsewhere).
Comment #57
EclipseGc CreditAttribution: EclipseGc commentedCan't reproduce this fatal locally, so I'm trying stuff...
Eclipse
Comment #59
EclipseGc CreditAttribution: EclipseGc commentedAnyone know what gives on this container serialization error?
Eclipse
Comment #60
martin107 CreditAttribution: martin107 commented@EclipseGc - I am just posting a comment now because timezones are a funny thing ... and you are maybe awake...
I have seen this a few times before and it was always because a "use DependancySerializationTrait" was missing from the thing that was simulating the container...
I am just off the bed, but I will track this down tomorrow, if that helps....
Comment #61
EclipseGc CreditAttribution: EclipseGc commentedIt only happens when you don't pass the $theme in the url. Very interesting, looking further.
Eclipse
Comment #62
Gábor HojtsyRe #47/7: opened #2359879: Session negotiation settings cannot actually be changed on the UI, the real problem is in the submission function it does not save to the right place (pre-existing to this patch, should be solved independently).
Comment #63
EclipseGc CreditAttribution: EclipseGc commentedOk, I spoke with dawehner for a bit and he gave me a hint. The context_mapping in the form_state could have things in it we don't want to cache, and we only need it during build of the form array, so I set it to an empty array before returning the form. Let's see how this goes this time.
Eclipse
Comment #64
EclipseGc CreditAttribution: EclipseGc commentedMoved this up just a tad which will make it available for blocks to become context configurable just like conditions are with relative ease.
throwing away the context_map during administration time before we return the form to prevent nasty things from being cached with the form.
Comment #65
EclipseGc CreditAttribution: EclipseGc commentedComment #66
EclipseGc CreditAttribution: EclipseGc commentedComment #67
EclipseGc CreditAttribution: EclipseGc commentedNeed to fix this test.
Comment #68
Wim Leers#41: Oh, absolutely. That's not what I was asking. I was only asking for a reply to #2349679: Support registration of global context, so that you at least don't do things that you'll have to undo when you fix that issue. Did you do that while working on this? Perhaps that doesn't make sense — I don't know Contexts well enough. Then again, how could I, with #2344045: ContextInterface needs documentation still open?
I'd almost say we should block this on #2344045: ContextInterface needs documentation, because how can anybody who wasn't involved with building Contexts properly review this without at least knowing what Contexts are, exactly?
#45: Why no interdiff?! This is the big chunk I should review, but I have to postpone now because reviewing a 80K patch just before calling it a day is not going to work out well.
Subsequent interdiffs look fine. Will try to review the entire patch tomorrow.
Comment #69
EclipseGc CreditAttribution: EclipseGc commentedSorry Wim,
I'm not sure the interdiff would have made a ton of sense since I was tearing out the ConditionPluginBag/Collection in that particular patch because I could only get the patch half working with that in there. Frankly, I put that patch up expecting everything to go nuclear, so when it came back with so few errors, it was a total surprise. I knew the lack of interdiffs was going to be a pain point and I tried to make sure interdiffs were provided from that point forward. Sorry.
I'll respond to #2349679: Support registration of global context now and I've been trying to stay on top of #2344045: ContextInterface needs documentation as I had time.
Eclipse
Comment #70
EclipseGc CreditAttribution: EclipseGc commentedI failed to document a method I added. I'll take a pass at the renamed test in my next patch.
Eclipse
Comment #71
Wim LeersHah! :D That makes sense. No worries about the interdiff. As I said, I'll try to review this patch later today. Great to see a new green patch.
Comment #72
EclipseGc CreditAttribution: EclipseGc commentedI looked at this test for a bit, and it's testing functionality of the ConditionPluginCollection. This patch a.) removes that, and b.) remove the behaviors it was expect in favor of a more straight-forward code path. So, with those things being true, I removed the last test, because the rest of what it did (configuring conditions and such) is well tested elsewhere both procedurally and through the UI.
I consider this very ready for review.
Eclipse
Comment #73
dawehnerIf you remove the call to setConfiguration you can drop the entire constructor.
Is there no other way to fix this? One way what we could do is to implement __toString for Translation
Can you please wrap that inside a getContextHandler() method?
Why the hell do we call
getContextDefinitions()
twice here?
Note: If we would document this function property:
@return \Drupal\Component\Plugin\Context\ContextDefinitionInterface[]
and you would not need this inline documentation.It seems for me that if (isset($contexts)) would be easier to read?
So we basically move the storage of the visibility out of the block itself into another level so each system using blocks would have to implement it again, but from a general perspective I totally agree. The visibility does not belong into the block itself. This was an API design driven by the UI
Feel free to document it.
Well, I am not sure whether this statement is entirely correct. The context is actually also part of the AccessResult object.
... We are so not sure everywhere whether we talk about conditions or executables (whatever the later one is), but a little bit more standardization would be maybe cool. Fell free to give counterpoints.
docs ...
For readability it seems to help a bit to extract that into a small helper method on the same class
Same here, small helper methods improves the visibility a lot and also makes it potentially easier to extract later.
PS: why do we check hasValue at the end again? Previously we assume that visibility is an array, so its either not empty or still an
This is such a sad story, well, maybe we can change in the future
I wonder whether we could move this onto a separate interface like
BlockConfigurationInterface
(No, I don't really like that name at all)One thing I don't understand is why this is not part of Core itself ... every implementation of a block like system would need to fire an event?
We could just pass along the array of contexts by reference, so usercode would never have deal with the event itself. In reality all of them will do a
getContexts()
and asetContexts()
right?I think an example for an empty context would be great here. Just add a small @code @endcode block and make lifes people easy
This is a bit confusing, can you add a comment why you can use the same thing twice?
I don't know but it feels wrong that the variant have to determine the available global contexts ... could it ask some helper method? Note: Mini panels would have to do the same stuff maybe as well.
Did you considered not changing the form parents by speciying #tree => TRUE?
This seems to be a bit out of scope ...
Comment #74
EclipseGc CreditAttribution: EclipseGc commented1.) Done
2.) True, but that seems like a follow up. I was just trying to fix something that was broken here and since I could call to available methods, that seemed the better thing to do for the moment instead of changing another class.
3.) Done
4.) Done, however, even if we did document that return properly, the ContextHandlerInterface expects the Core version of the ContextDefinition, which I didn't initially notice, so I actually updated that variable documentation inline to make phpstorm stop complaining.
5.) Done
6.) Yes all around, it also means that people implementing this now have the opportunity to create their own UI w/o having to spend effort and time destroying our UI first.
7.) Done
8.) Not my comment, I just moved the comment with the code. I've asked Wim to comment (he might beat me to commenting) but I will note that we should re-arrange this in a follow up to execute block access before block visibility since visibility is user configurable and could get very hairy, and access is hard coded. I know it used to work this way and was inverted before conditions were in charge of block visibility, but given the final result here, regardless of this patch, we should revert that change and check block access first.
9.) The ExecutablePluginInterface is the interface which the ConditionManager implements. We typehint by one, but we expect an instance of the other. The Action plugins should also be using ExecutablePluginInterface. This was debated and negotiated at length with fago. I know it's weird, but it's how the current system works, and there are "reasons". I was mostly trying to make many parties happy when that code was being written, so it is what it is. PageManager and Rules both have plans for those systems, and I was just trying to make sure we wouldn't need completely different plugin types again. *shrug*
10.) Done
11.) & 12.a.) Fair enough. Not to sidetrack my own issue here... but why isn't $form passed by reference to the validate() method? That gives me pause. Also the "hasValue()" check is just me being uber-paranoid.
12.b.) Yes, sad story, but it was my best solution to a bad problem. If we can swap out the UI later, we can remove this... we could also actually #require the appropriate values in the conditions, which is another WTF for me.
13.) I don't think that's necessary. The block entity (for which this is the interface) is the holder of all this sort of logic regardless of what we do, so having these methods as part of this interface is the cleanest and most straight-forward solution. No other object has to hold this stuff ever.
14.) There are certain contexts which are global in nature and others which will be specific to blocks. There's an issue for the global ones so this patch just sets that patch up to make it much easier. In short, language and current user will move out of block specific contexts and node will likely stay since other systems will determine the availability of route-based contexts in a completely different way.
15.) Actually most will just do a $event->setContext(). I chose not to send this to yet another abstract method because interacting with the event is literally just calling the setContext() method to add your context if necessary. I don't think they should ever have to call getContext() on the event, that's really for the class that dispatched the event to do to get all the contexts set by the subscriber. I just went for the least abstracted scenario here since there's only one method the developer has to interact with.
16.) Done
17.) Done
18.) Actually I think this is where this belongs. The Variant can frame the entire block placement/context discussion. That's actually a huge part of its purpose. In the case of panels (not mini-panels) Panels should provide a custom Variant class as well, and would want to do something like this (but as alluded to elsewhere in my comments will probably have a different methodology for getting much of it. Putting this in FullPageVariant, in my opinion, contains the approach that core needs in a place where it is unlikely to influence the design of the system in such a way as to make contrib have to "undo" things. They can replace this class with their own, but they'd have had to do that anyway. As far as mini-panels goes, that should be implemented as a block, so it should get contexts pushed down to it by whatever methodology the rendering Variant chooses. Hopefully that made sense, I'm open to debate on this one, but this is how I see it currently.
19.) Yes, in fact my early patch(es) did exactly that, but as Tim Plunkett points out earlier in this comment thread, 'settings' is specific to the block plugin, and we're separating visibility from that, so we need to move the form elements as well.
20.) The verb tenses were wrong and my ocd kicked in. do I HAVE to remove it?
Eclipse
Comment #75
dsnopekI read through the interdiffs since I last tested this patch, and nothing jumped out at me. That said, I'm really not qualified to do a line-by-line review of this code. :-)
However, I did manual testing again, and everything worked great! In my previous review/testing in #48, I set a block to only be visible on "<front>" and it wasn't working. In my testing with the latest patch from #74, it worked as it should!
I'm really looking forward to this getting committed, and then the expected follow up on #2349679: Support registration of global context! This will be a major win for Panels (and just Blocks in general).
Comment #76
dawehnerCan you please open one :)
Well I prefer it the other way round, see
EntityTypeEventSubscriberTrait
but yeah you seem to have more words so you win, HAHA.ctrl+alt+z is the shortcut for me to undo a specific hunk in storm. This is really handy once you use it everyday.
Comment #77
EclipseGc CreditAttribution: EclipseGc commentedRe:2.) I was wrong, and the code in this patch handled it wrong. The ContextDefinition annotation class should have unpacked this before it ever ended up in the ContextDefinition implementation class. My bad. Other Plugin Annotations extend the Plugin class which includes a protected parse() method that unpacks this stuff for us. I added a simple bit of code to the ContextDefinition annotation class that will properly get() the translation & cast it to a string. I think it should still pass tests. No follow up for this one.
20.) ok removed.
Eclipse
Comment #79
EclipseGc CreditAttribution: EclipseGc commentedmissed a semi-colon
Comment #80
EclipseGc CreditAttribution: EclipseGc commentedhere's the interdiff for documentation purposes.
Comment #81
dawehner@EclipseGc Remind me, why did you not implemented __toString in TranslationWrapper?
I think having that one single method would be less change than fixing the one instance, isn't it?
Comment #82
EclipseGc CreditAttribution: EclipseGc commentedYeah, a __toString() implementation could still be great for us to have, filed a follow up: #2362727: Implement __toString() on Translation Annotation That being said, the changes I made to this patch are the more appropriate changes for this particular problem until that patch lands, so... Nothing to change here.
Eclipse
Comment #83
EclipseGc CreditAttribution: EclipseGc commentedComment #84
Gábor HojtsyI click reviewed this patch to see how it applies to language conditions on blocks. What I found is the patch significantly changes the conditions UI by not displaying the summaries of selections in the left hand pane. I don't believe that is intentional but would need to be fixed.
I also noticed that if I have an out of the box English Drupal install and install language module I get a totally empty language condition option for blocks. No options. I thought this would be a regression of this patch, but apparently it is there without also (reproduced). We may or may not want to resolve that here. If not, I should open a followup issue.
Finally there is no explanation on the language type selector. Before #2278541: Refactor block visibility to use condition plugins this used to have a "Languauge type" label. I think its not very straightforward without a label). It also used to be a radios list but I think the select box works better.
Now looking into some test for the language types but feel free to do anything in the meantime. Test will be self contained.
Comment #85
EclipseGc CreditAttribution: EclipseGc commentedGabor,
Can you give steps to reproduce the blank condition?
Eclipse
Comment #86
Gábor HojtsyReproduction steps for empty conditions: Install Drupal standard profile (in English). Turn on language module. Go to edit any block.
Comment #87
EclipseGc CreditAttribution: EclipseGc commentedDanke!
Comment #88
Gábor HojtsyHere is the test for block language visibility. Not uploading the complete patch because I cannot figure out for the life of me why the drupalGet()'s will not take the query argument I asked them to use. It just ignores that... I think it would fully pass if it would take it, there is one fail exactly because it does not actually request a page with the get arguments I asked it to :) Hope someone can help figure this remaining bit out, it does not have much to do with the patch actually...
Comment #89
EclipseGc CreditAttribution: EclipseGc commentedGabor, I worked against your test a bit and manually went through the same steps. Locally, when I manually reproduce your steps, the "session" detection on content doesn't save the first time through and I have to resave the form again. Once I do this, it saves the session settings properly and manually following the same steps as your test works. I think there's a bug in that form.
Let me know
Eclipse
Comment #90
EclipseGc CreditAttribution: EclipseGc commentedOk, I added Gabor's patch in (no interdiff since he essentially provided that) and fixed the condition descriptions.
With regard to the empty Language condition, I think we should provide a core-specific solution much like we do with current theme and simply remove it from the list if there aren't multiple configured languages. Gabor, does that seem sensible to you?
I've worked against these tests quite a bit, and the english language gets passed as a context instead of french for the one failing test. This doesn't happen outside of tests, so I'm not sure what the issue is. :-(
More this weekend I guess.
Eclipse
Comment #92
EclipseGc CreditAttribution: EclipseGc commentedOk, hunting down my theory about language_content further, I decided to flip flop the tests and see if that passed, it DOES, which means there's an issue of some sort with the form. Again, walking through this manually in a fully installed instance, I can make either work, but I have to save the form twice for language_content to work. Anyway, this should show that the code in this patch is actually working and doing what gabor needed, as well as exposing some functionality contextual plugins where intended to have, all around win I hope. We still have the open question about the language condition when there are no configured languages. I'm still leaning heavily toward removing it from the list when the language count is less than 2.
Eclipse
Comment #93
Gábor HojtsyWould be good to kbpw why this works now vs. not before. Also the code comments are now incorrect.
As for not having the condition if !languageManager()->isMultilingual() that makes a ton of sense.
Comment #94
EclipseGc CreditAttribution: EclipseGc commentedThis should break a few tests, but identifying them this way will be faster than doing it locally.
Eclipse
Comment #96
EclipseGc CreditAttribution: EclipseGc commentedThis should fix it.
Eclipse
Comment #97
Gábor HojtsyGood workaround for the bug with the form (that you chamge the UI settings instead of the content settings). I also experienced the content form bug on the UI. Opened #2364171: Enabling and configuring content language negotiation does not work at once for that. No need to hold this back in any way. We have great test coverage for the multiple context options on languages here. It proves the regression is resolved by the patch.
I cannot unfortunately vouch for the architecture of the rest of the patch.
Comment #98
dawehner+1 for moving up the plugin settings.
Can we please add a IS in order to make it possible somehow to describe people what they have to do, when they update their existing site?
Do we really want to typehint here different?
Comment #99
tim.plunkettI'm going to spend some time reviewing this during the week.
Comment #100
EclipseGc CreditAttribution: EclipseGc commentedThere is no ConditionManagerInterface, so yes we want to type hint by ExecutableManagerInterface, however we fully expect the ConditionManager to be passed to us. If we want to hint that variable the same way we hint the constructor, I can understand that, but this is the most accurate currently.
Issue Summary should be all up to date. I'm afraid resaving blocks is probably currently required in order to get the context behaving properly, however I'm not sure if that matter horribly since we're not supporting upgrade paths yet. I'll get started on a Change Notice though.
Eclipse
Comment #101
EclipseGc CreditAttribution: EclipseGc commentedComment #102
dawehnerSo ...
Comment #103
EclipseGc CreditAttribution: EclipseGc commentedChange record added: https://www.drupal.org/node/2367239
Comment #104
tim.plunkettI think this is really close.
I rerolled for a conflict in HEAD, and made two small tweaks (see interdiff.txt)
Shouldn't this happen before this ->access() call?
This goes against how all plugins are written. They have default config, and you don't need !empty checks when using it. I think you're just doing this to get around export issues, ones we don't have in HEAD.
These too.
Comment #105
EclipseGc CreditAttribution: EclipseGc commented1.) Good catch!
2/3.) So that's not the reason at all. The reason is that as conditions stand in core today, their defaults are added to the plugin config on __construct, and this makes them exceptionally difficult to determine if they existed before this moment, which is made doubly bad by the block visibility implicitly displaying all conditions forms all the time (instead of explicitly asking for the visibility conditions you need). With that in mind, I agree that they could function this way if we had a different UI, but as it stands now, I did quite a lot of work to make it possible to determine if we needed various conditions values saved (since context mapping occurs if we do have legit values, and this can cause false negatives for block visibility if we have contexts mapped into a condition we don't actually want to have run.
Again, if the UI for this were not core's traditional UI and were instead one where users specified the conditions they wanted to have run, I wouldn't have had to do this particular thing. I can't say I'm a big fan of setting the configuration from defaults on __construct() in any event though. Manually checking empty config and falling back to sane defaults would be way safer in terms of understanding a plugin's state. Sounds like that ship may have sailed. I'm happy to discuss this further, though if we agree that block visibility UI should have an overhaul before 8.0.0, then we could focus on that next and get these back in line with other config plugins. Thoughts?
Eclipse
Comment #106
tim.plunkettI went ahead and tested the revert of 2/3, and it worked fine. I also reinstated the ConditionPluginCollection.
But while digging in, I had some larger questions, especially since this is now duplicating some code from page_manager.
Hopefully I'll be able to catch up with @EclipseGc tomorrow or at BADCamp to resolve them.
Comment #107
kim.pepperA few nitpick doc cleanups.
s/direct use/directly used/ ?
Missing language manager @param.
Missing full namespace.
Comment #108
tim.plunkettI figured out part of what was bothering me.
BlockForm is using
$form_state->set('context_mapping', $contexts);
as a temporary place to store context *objects*.However, the rest of the core code base, and much more so page_manager/rules, is using it for a mapping of what context represents what.
It even has a config schema entry for it.
For example, if a page_manager page is taking over /user/%, and you are placing a block to show a user, should it use the current user or the one from the URL? 'context_mapping' would store an associative array of strings, like
'user1' => 'current_user'
.Block might not have to deal with that, but it could. And regardless, for the temporary storage of context objects, it should not abuse 'context_mapping'.
Also note the difference between
$form_state->getValue('context_mapping')
and$form_state->get('context_mapping')
.That's not changed in this patch. This addresses @kim.pepper's feedback and tweaks the existing code to match existing naming and responsibilities better. For example, ContextAwarePluginBase shouldn't deal with FormStateInterface directly.
Comment #109
tim.plunkettStill a couple things left to resolve, but at least I was able to remove the weird unsetting of contexts (instead of using the persistent FAPI storage, using the temporary storage).
Also renamed clearInvalidConditions and made it more obvious by returning a boolean.
Finally, fixed the misuse of the name "context_mappings" and used "gathered_contexts" instead.
Comment #110
jibranSO who will RTBC it?
Comment #111
tim.plunkettIt's not ready yet, I have more updates pending after a good conversation with @EclipseGc at badcamp.
Comment #112
tim.plunkettWe discussed readding the usage of the collection for the non-form use cases.
Comment #114
tim.plunkettAh, we have a systemic problem with copyFormValuesToEntity when used with EntityWithPluginCollectionInterface. Fixing that generically, borrowing straight from my workaround in page_manager.
First interdiff is for that fix, the second is for the changes after rerolling for #2367579: Move retrieval of visible blocks to a standalone service.
Comment #115
tim.plunkettComment #116
tim.plunkettI'm trying to rewrite page_manager to use this new code, and I really feel like addContextAssignmentElement and saveContextConfiguration are in the wrong place.
Hopefully I can track down @EclipseGc and discuss this more.
Comment #117
tim.plunkettLet me dump some thoughts here:
The way I have addContextAssignmentElement in page_manager is that it is a method on trait that is to be used by a form.
It takes a plugin and an array of contexts.
This way, any form can choose to add this form element for a plugin.
But in this patch, it's a method on the plugin directly, and each plugin would have to decide to call it.
In the case of page_manager, we want to make the blocks context aware. We can do that TO a block because our call is external.
If we were to only use the approach from this patch, every plugin that ever could be context-aware would have to call these methods themselves.
One option would be to make it public, but then what interface will it live on?
Comment #118
EclipseGc CreditAttribution: EclipseGc commentedWell, I mean yes and no. Yes a plugin could choose to NOT call it, but it would have to NOT inherit from the base class provided by core. If someone wanted to code around the base class they could end up with contextual plugins that don't properly invoke these methods, but I don't think it's really that big a problem because this is the sort of thing that our plugin base classes usually provide. In the case of blocks this is especially true because the base class adds form elements for caching, label, etc and this would get added in exactly the same way as it is in conditions i.e. BlockBase::buildConfigurationForm() & BlockBase::submitConfigurationForm(). Every plugin won't have to opt-in to their proper context form elements. I didn't have to change any Condition plugins in this patch in this regard. The same will be true of blocks in the follow up we file for that purpose, and it's the plugin base class that intercedes on our behalf in both instances.
This doesn't mean I'm not open to discussing an alternative architecture for this problem if you think your is just vastly better and more understandable/usable, but the approach I pursued here was designed to put the work on plugin type developers and to be invisible to individual plugin developers.
Your interdiffs look good at first glance. I'll apply the patch when I get home to just follow the code and make sure I grok it all still. We can discuss these other issues and see if we can get on the page early this week.
Eclipse
Comment #119
tim.plunkettTalked with @effulgentsia and @EclipseGc about this, I think I finally found the flow I wanted.
Comment #121
tim.plunkettSilly mistake.
I don't think this needs any explicit committer feedback, just a solid review.
Comment #122
tim.plunkett@EclipseGc expressed concern about the core plugins we know about being able to present their entire form as-is. We still want to leave addContextAssignmentElement on a trait to allow contrib to use it, but core can handle itself.
Comment #123
EclipseGc CreditAttribution: EclipseGc commentedSo yeah, I'm pretty happy with that.
Eclipse
Comment #124
jibranDo we need a change notice for this?
Can we please ask @fago or @effulgentsia to review it?
Comment #125
EclipseGc CreditAttribution: EclipseGc commentedI already wrote a change notice, it is in draft mode. See #103.
Eclipse
Comment #126
Wim LeersOver at #2370667: [Meta] user/password flame graph, the block condition checking is showing up to be extremely expensive. For that simple
/user/password
page, we're seeing the following, with all percentages being percentages of the total page load time:So 6.1% of total page load time of a super simple page is spent on handling block condition/contexts. This will likely be much worse on more complex sites that have many more blocks set up.
I think we want to make sure that this doesn't cause a regression.
Also: it feels very bizarre to me that EclipseGc would be allowed to RTBC what is mostly his patch… somebody else should also review it.
Comment #127
EclipseGc CreditAttribution: EclipseGc commentedWe should also re-flip-flop the block access and conditions check. But that should be a follow up.
Eclipse
Comment #128
EclipseGc CreditAttribution: EclipseGc commentedPlaying with this, I ran into another bug, so definitely ++ing the NW status.
Eclipse
Comment #129
dawehner@Wim Leers
The silly fact that we do N*M context getting is just silly, totally agreed. Therefore I opened up #2354889: Make block context faster by removing onBlock event and replace it with loading from a ContextManager which is not part of this issue
though.
Comment #130
EclipseGc CreditAttribution: EclipseGc commented[deleted]
I'm rerunning all these numbers. Let's pretend I didn't post anything in this regard yet. :-)
Eclipse
Comment #131
tim.plunkettThis addresses the two bugs @EclipseGc found.
First was that removing a visibility condition through the UI was not working.
The second was that no-op, unconfigured conditions were still being stored in the entity.
I added test coverage for both of those cases.
Comment #133
tim.plunkettSelf-review!
This is moved. Still block specific, but just entity not plugin.
These two methods elegantly solve the problem of not being able to rely on ConfigurablePluginInterface.
This is still present, just in a different form.
This is one of the major parts of the fix. We no longer blindly add all conditions to the plugin collection for runtime usage, but instead gather all plugins in the form instead.
This is moved from the plugin to the entity
This is a very useful place to put the contexts. Also #2371853: Add more helper methods around temporary FAPI storage would make this easier to read and understand
This approach is borrowed directly from page_manager, and should have been added alongside the EntityWithPluginCollection originally. It prevents block, image, page_manager and all of contrib from having to remember this VERY important step.
This class is 1:1 out of page_manager, and mimics what @EclipseGc had written, just changing that you pass the plugin to it.
There are a fair number of changes like these, just because visibility is moving up a level.
This is a much better name, and less coupled to the conditions use case. A good step towards #2349679: Support registration of global context
The split from one event to two is extremely important, I'm glad we're tackling that here.
Many of them will just call the active handling during the administrative, but not always. This is why we need this
Comment #134
EclipseGc CreditAttribution: EclipseGc commentedJust for the sake of moving this along, Tim and I are both just waiting for an RTBC on this.
Assigning to effulgentsia to see if I can prompt a review. :-D
Eclipse
Comment #135
tim.plunkettRerolled on top of #2352155: Remove HtmlFragment/HtmlPage.
Comment #136
effulgentsia CreditAttribution: effulgentsia commentedStill digesting this patch. Some preliminary thoughts/questions:
At first glance, this seems like adding unnecessary coupling. Why should the plugin be in any way responsible for the context mappings? Shouldn't that be entirely managed from the outside? Looking at the patch, it doesn't look to me like these are called in many places, and where they are, I think the caller can manage the storage/retrieval itself without delegating to the plugin. Or am I missing something?
Looks to me like we always return the latter. Do we still need to support the former?
Does the word "Available" add any meaning here? BlockRepository::getVisibleBlocksPerRegion() is simply passed a $contexts variable (not $available_contexts), and that's what it passes to $block->setAvailableContexts(), so I'm confused what "available" is disambiguating. AFAICT, a block entity has no contexts that it deals with other than the available ones, or is that not true?
Comment #137
tim.plunkett1) I know we discussed this some at BADCamp, and while it may seem unnecessary, this is definitely codifying what is currently done in HEAD.
Another object, like the block entity, could certainly maintain this mapping for the visibility. But then you'd end up with a YAML structure like
Instead of
Which means one less custom schema, one less thing to be sure of adding and removing when a condition is changed, and one less new property on the entity.
This may not seem like a lot, but when considering page manager, they have access conditions, selection conditions, and visibility conditions. That becomes a lot to manage.
Also, a condition once configured still requires its context_mapping in order to function. I would consider that part of its configuration.
2) We need that for iterating over the plugin collection, which we do in BlockAccessControlHandler for example
3) Honestly I'm not sure. @EclipseGc, any opinion?
Comment #138
effulgentsia CreditAttribution: effulgentsia commentedPartial review. Posting it before dreditor decides to crash.
I think a better example would help clarify this more. Perhaps 'user' and the possibility of multiple things mapping to it (like 'current_user' and whatever the other ones are named).
Let's add a @todo to the __toString() issue, so we remember to remove this ugliness.
We either need to keep the entire comment, or remove the entire comment. If we remove the comment (since condition checking is now done elsewhere), we should also remove the setCacheable(FALSE) that's at the end of this function.
Comment could be improved to explain:
- why default config means that context mapping wasn't used
- why remove the context mapping from the comparison (performance? some other problem?)
Minor, but can we do an array_intersect_key() instead of repeated in_array() calls?
Let's move this comment to just above where we actually call setCacheable(FALSE). Bonus points for also filing an issue and adding a @todo linking to it.
The combination of setCacheable(FALSE) and cacheUntil...() is very confusing. If the idea is to ensure we do the latter when we resolve the @todo about the former, then perhaps that would be better to express in the comment rather than in the code?
A comment explaining why we're setting contexts on both the entity and the plugin would be helpful. For example, is it because there might be use cases of the plugin implementing additional access logic based on context? If not, and the plugin context is only for preparing the plugin prior to returning it, perhaps we should move its context assignment to after the access check to clarify that?
I'm confused by this sentence.
Comment #139
effulgentsia CreditAttribution: effulgentsia commentedThe rest of the patch looks good to me. Just a couple other docs nits:
This could be improved/clarified. If someone else wants to take a crack at doing so, go for it. Or else, I might when I have a chance.
Can be removed.
Comment #140
effulgentsia CreditAttribution: effulgentsia commentedIf possible, it would be great to have confirmation from @Gábor that there is sufficient test coverage here for the original multilingual problems that started this issue. I think there is, but I could easily have overlooked something.
Comment #141
effulgentsia CreditAttribution: effulgentsia commentedUnassigning myself until #136.3 and #138 - #140 are addressed.
I'm ok with #137's answers to #136.1 and #136.2, given that those are pre-existing conditions of HEAD so not part of this issue's scope to change.
Comment #142
tim.plunkett#138
1) fixed
2) added
3) I opened #2375689: BlockBase::blockAccess() should return AccessResult instead of a bool, because we still have to mark this uncacheable.
4) I tried to rewrite this.
5) Rewrote this, and added a unit test (should have done that earlier!)
6) Moved this.
7) Added an @todo pointing to #2375695: Condition plugins should provide cache contexts AND cacheability metadata needs to be exposed and adjusted the code.
8) Actually, this is redundant. I think we ended up with two calls after the rerolls for BlockRepository and HtmlPage. The setAvailableContexts() call is so that BlockAccessControlHandler can retrieve them, so it can apply them to the plugins. Fixed.
9) Tried to fix this some. An additional thought about this, maybe the block can passed to this event, which might help resolve #2374295: Get the block context in a condition plugin?
#139
1) Not sure what to do with this, leaving for @effulgentsia
2) Removed
#140
@gabor wrote the test coverage, and it seems complete to me.
Comment #143
EclipseGc CreditAttribution: EclipseGc commentedSo, neither Tim, nor myself added this hunk, it was just moved from where it was. HEAD does this today. Are we sure we want to change it?
Whoa!? When did we add this? I mean I'm all for it, but we haven't expose the UI for block contexts yet, so this won't function till we do.
136.3: get/setAvailableContexts(). Let's use the same naming convention the events use and call this get/setActiveContexts(). I don't think there's a custom method for this in the form for get/setAdministrativeContexts(). We might consider making a trait out of either or both of these in a follow up.
138.3: Conditions should not care about cache-ability of access. They have no notion of this 3-option system. Conditions return TRUE or they return FALSE. A condition does not have 'no opinion'. The block system cares about cache-ability, and should be the one to determine whether or not a user has access to this block. Conditions just help to inform it what decision to make. We've discussed this before I think. Think of conditions outside of blocks before doing stuff to it. Rules has no need of condition cache-ability independent of what it's own wrapping implementation may choose to do. Let's not get in the way of that by being more opinionated than a simple boolean. (If I'm missing the point here, lets spend some time bringing me up to speed in irc or something)
138.7: As I said in the code comment above, neither tim, nor I added this. This is what HEAD does currently. I just moved it to its current location. In contrast to my opinion in 138.3, I think it makes perfect sense to annotate the cache contexts, however I'd like to understand how this is different from the actual context itself. I suppose this is probably the difference between varying on role and varying by user?
138.8: As I said in the code comment above, this actually applies contexts to the blocks. It won't do anything till we have a UI in place for mapping contexts to the block. I'm fine with removing it, but we'll be re-adding it in a follow up. Not sure how/when it got added here. I don't recall doing that. :-D
138.9: Tim, I've been thinking about that particular issue for a while. Since we're passing the array of contexts in (not by reference) I think each block could add its own block entity to the context array for its own conditions. Definitely follow-up worthy, but I don't think it changes the implementation here at all.
Yes, Gabor wrote the test coverage, so I think we're covered.
Eclipse
Comment #144
Gábor HojtsyYeah the tests still look intact/adequate for multilingual blocks in the patch that I wrote and @EclipseGC helped me fix (after we found #2364171: Enabling and configuring content language negotiation does not work at once). This also spawned #2359879: Session negotiation settings cannot actually be changed on the UI so pretty useful side effects for the multilingual system.
The other bug we found here, that the language condition showed if we only had language module enabled but not yet multilingual was also fixed. Is there a general test for this? My test was not extended for it. I think we can remove a language at the end in my test and check that the language condition is not there anymore (it only has 2 languages to test if I remember correctly). Settings to needs work for that, should only be a few lines of test code hopefully.
Comment #145
tim.plunkettAdded a test for the multilingual bit.
Renamed getAvailableContexts to getContexts
Adjusted how buildVisibilityInterface works, with @EclipseGc's blessing.
Comment #146
EclipseGc CreditAttribution: EclipseGc commentedInterdiff looks great!
Eclipse
Comment #148
tim.plunkettNoob mistake, I uploaded those patches in the wrong order.
Comment #149
EclipseGc CreditAttribution: EclipseGc commentedRTBC?
Eclipse
Comment #150
effulgentsia CreditAttribution: effulgentsia commentedThis addresses #139.1 and the other instance of #138.7.
Comment #151
effulgentsia CreditAttribution: effulgentsia commentedDo we have other places where we use a pattern of having a $form parameter actually be only a part of a form? For WidgetInterface::settingsForm(), we pass a complete $form in, but the function returns a $element that the caller inserts into the right place. Should we change this to the same pattern as that?
Comment #152
tim.plunkettLook at the line directly above the hunk you quote.
We do this in several places, where the subform doesn't need to know anything about the parent form.
Comment #153
EclipseGc CreditAttribution: EclipseGc commentedI'm great with everything I'm seeing here. Effulgentsia asked me for a "Number of Function Calls" diff from xhprof. I'm doing this in a vm, so my wall times are worthless but the function diff is static. All tests were performed against /user/login as an anonymous user.
Fresh Install of Drupal 8 vs D8 with this patch: (Blocks as configured by standard profile out of the box)
Powered by drupal block dependent upon authenticated user:
I tend to think there's a performance improvement in here as well, but w/o a good environment to actually test it on, I can't prove it. Just given how things were re-arranged, I was expecting it.
Eclipse
Comment #154
effulgentsia CreditAttribution: effulgentsia commentedI think all feedback has been addressed.
Comment #155
Gábor HojtsyI agree the added test coverage looks good :)
Comment #156
Wim LeersFirst: the numbers in #153 look great! :) I can't wait for #2370667: [Meta] user/password flame graph to be redone once this lands, looks like this addresses most of the poor performance we found there, as I described in #126. AFAICT the reason is that we now dispatch
BlockEvents::ACTIVE_CONTEXT
once, rather than dispatchingBlockEvents::CONDITION_CONTEXT
once per block? Please confirm.Second: I'm very glad to see the
visibility
key moved out of thesettings
key in block config entities. I have an off-topic question about that for you all: a long time ago, when I was working with msonnabaum and others on refactoring block rendering and making them cacheable, we did the same for thecache
key as was the case for thevisibility
key: we put it undersettings
. I remember disliking it and running into nasty things with Form API because of that (since the cacheability settings are shared, but the other settings aren't). Do we want to movecache
out ofsettings
, just like this patch does forvisibility
?I also went over the patch once more, to understand the performance improvement above, but in doing so I found a bunch of nitpicks. Sorry.
Typehint to
array
, to match the docblock.s/its default/its default configuration/
I find these fiveish lines of comments very difficult to understand, but I think it's actually a trivial thing. It's a case where explaining it clearly is more difficult than doing it. So it's probably ok.
Needs fully qualified interfaces.
Why not use strict equality?
Either the TODO should be resolved or there should be an issue for it?
Should be a fully qualified class.
Comment #157
EclipseGc CreditAttribution: EclipseGc commentedThis patch takes care of everything except 156.4 since we never really use strict equality for strings since they don't need to be evaluated this way.
Yes, the big performance boost here (at least in number of function calls made) is largely due to the fact that contexts are only requested once and not per block.
Yes I'd love to explore moving the cache form element out of the block settings too. I really firmly believe that's a completely separate task and we need to discuss that, but that's a completely separate topic.
Eclipse
Comment #159
EclipseGc CreditAttribution: EclipseGc commentedMy bad.
Eclipse
Comment #160
EclipseGc CreditAttribution: EclipseGc commentedComment #161
Wim LeersOf course! That's why I said it's an off-topic question :)
Comment #162
alexpottCan remove
use Drupal\Component\Plugin\ConfigurablePluginInterface;
getContextDefintions() returns
\Drupal\Component\Plugin\Context\ContextDefinitionInterface[]
but getMatchingContexts() accepts\Drupal\Core\Plugin\Context\ContextDefinitionInterface
. I guess that the latter should be changed to the less specific interface.$definition
Not used
Use $type_key looks fragile and has a high potential for collisions, no? Shouldn't we be keying by provider and a key? ie. something like
language.$type_key
or maybeblock.$type_key
since this is provided by the block module. I guess that context names don't have a namespacing strategy in HEAD either. This looks fragile.Not used
Unused.
Comment #163
EclipseGc CreditAttribution: EclipseGc commentedOk, punted on 162.2 since that exists in HEAD, I fixed all the rest. The main change here is around 162.5. Alex asked for a provider.context_id notation. I set these up as though they were from the code base that will eventually provide the events. These are all in block currently, but as we transition to a more global context setup, these will move. It doesn't hurt us either way though, and they're more specific now.
Eclipse
Comment #165
EclipseGc CreditAttribution: EclipseGc commentedLet's see how this one does. Failure was weird on the last patch. :-S
Eclipse
Comment #166
tim.plunkettFine with #163/165, thanks for jumping on that @EclipseGc
Comment #167
alexpottThis should have an 8.x to 8.x change record since if you've got a module that adds a listener to
BlockEvents::CONDITION_CONTEXT
that will be very broken by this patch.Comment #168
EclipseGc CreditAttribution: EclipseGc commentedOk, I've written a change notice previously, added before/after for the events.
https://www.drupal.org/node/2367239
Eclipse
Comment #169
alexpottSomething extra came along...
AggregatorFeedContext
- true interdiff between 159 and 165 attached... talked to @tim.plunkett and @EclipseGc in IRC and just removed core/modules/aggregator/src/EventSubscriber/AggregatorFeedContext.php on commit. This issue is a major task that will improve performance significantly and the disruption it introduces is limited. Per https://www.drupal.org/core/beta-changes, this is a good change to complete during the Drupal 8 beta phase. Committed 633d842 and pushed to 8.0.x. Thanks!Comment #171
EclipseGc CreditAttribution: EclipseGc commentedComment #172
Gábor HojtsyThanks folks! It is really great to see this not only fixing a language visibility regression but cleaning up the API and making it better for page manager and rules. This rules :) Thanks for your dedication!
Comment #174
Xano#88 added
$this->assertNoField('visibility[language][context_mapping][language]', 'Language type field is not visible.');
inBlockLanguageTest
, which fails in #2647464-9: Condition plugin configuration forms depend on their parent forms. I don't quite understand why we are testing for the absence of the language context configuration elements. Can someone explain?Comment #175
EclipseGc CreditAttribution: EclipseGc commentedHaving not looked directly at that test, I can speak generically about the context system. Conditions and Blocks leverage contexts & mappings, but the form element only shows up if more than one context of a type is available. Let's do a scenario:
I am using the node_type condition to check if a node is of bundle "article". When the only node context I have is the one from the URL, there's no reason to prompt the user for some mapping... It can only be that node. But if we have two different node contexts available, I need a form element so that they can tell me which one we'll be testing against. In this way, the form element comes and goes and it's completely valid for it to do so.
Eclipse
Comment #176
Gábor Hojtsy@Xano: extending on what EclipseGC said, the tested case is when the content language is not customized, so only one language type is available (interface language) that is configurable. In that case, the language type selector should not show up, because all blocks tie to the interface language. Once there are more language types available, blocks expose that, so you can say which type of language (interface, content, etc) they should show/hide by. It would not be nice to show a one item select box for the base case :)