Thanks for the explanation, I'll have to take a closer look then.
Will you be in Vienna next week? If helpful, we could also discuss this there.
No, sadly not. I fully intended on going but then I had a career change and I wasn't sure how well everything would be going by the time Vienna came around, so I took the safer option and decided to sit this one out.
The issue I have with node grants, and why Group does not support it, is that it only works for nodes. Instead, Group leverages the "entity_query" query tag to add access for all entity types to queries on your site.
@drunken monkey, is it then safe to assume that search_api does not leverage query access for entity types other than nodes?
I had a brief look at the "Content access" plugin and it seems to add the grants to the index? In that case, facet counts won't work with Group or any other entity type leveraging query access as the node entity type is the only one using a grants system.
One way around this that I can think of is to grab the resulting IDs from the index, run an entity query with access checks turned on and then use the intersection to show results and counts. Not sure how easy that would be to pull off, though.
@korinna.mucsi Nothing has been agreed upon yet. We're still in the process of discussing things.
Different approach, actually check for NULL. Will open an MR.
Adjusted the MR to pass phpcs and phpstan. Can confirm this seems to work on my end. The tests are failing, but that was to be expected as the asserts have not been adjusted to the new code.
Also, the fact that it's a unit test complicates things now that we're using the query condition group. So either we mock that whole thing or witch to a kernel test.
kristiaanvandeneynde → made their first commit to this issue’s fork.
kristiaanvandeneynde → created an issue.
Thanks, committed. Also backported to 2.x, should have taken care of all the group_content machine names while doing so.
I'm going to close this as outdated. The test in question is almost entirely empty nowadays and, as you mentioned, runs fine right now.
That just moves the problem to Opensearch itself :D
https://docs.opensearch.org/latest/vector-search/ai-search/hybrid-search...
kristiaanvandeneynde → changed the visibility of the branch 3548663-queryparambuilder-setting-a to hidden.
kristiaanvandeneynde → created an issue.
Added a cherry-pick branch, didn't give any conflicts on the files that matter so that would make me assume we did not change any code between 3.3.x and 4.0.x in hooks. Still needs to be verified, though.
Also might need to bump minimum D10 version so the LegacyHook attribute is supported.
I think there might be an issue with this approach, since queries often happen during the render array build and before the actual render.
Sorry, you're right. I was living in a perfect world where (almost) everything is a placeholder or pre_render callback. Something I'd like to see one day :)
But that could still work, though. If we know we're building something renderable, we could call the service in advance with the cache keys that we are setting on the very thing we're building. And then everything I said in #21 could still happen. I.e.: We'd be able to know exactly which metadata came from which render array and attach it back when going through the render process.
kristiaanvandeneynde → created an issue.
Also just realized v3 still supports D10, which had no OOP hooks. So that might complicate things a lot.
I like #19, but we indeed can't wait until the very top level to add that cacheable metadata back in like @catch said in #20.
What if we add a service like in #19, but call and flush it whenever we're about to attempt to write to a cache in Renderer? This means that we would be able to add the metadata to the very render element that triggered the query in the first place. It could then bubble up or not just like it would for any normal render scenario.
E.g.: We have a page with a region with a block with a view with some nodes. The view triggers a query, so when we start rendering that the service knows the current cache key is the view's and when Renderer gets to trying to store the view with said cache key, our service returns metadata which we add to the render array.
It then gets written to the cache (or not) and bubbles up (or not) based on normal Renderer flow. Only thing to look out for is that we do not poison this system by adding cacheable metadata to db cache lookup queries as that could get really ugly.
Rather than trying to convert them all over again, could we not take 📌 Convert hooks to OOP hooks with attributes Active and backport that? The only thing to look out for then is features that were new in v4; we definitely should not introduce those in older versions.
The maximum number of seconds allowed to run indexing per index. Batches that were started before the limit was reached will run to completion and may therefore slightly exceed the limit. Defaults to -1 (no limit).
Is this better? It seems more clear about the potential impact.
Either way, feel free to commit your or my version without further feedback. I consider this issue RTBC. I'm not a fan of delaying things over nitpicking how we should phrase a comment :)
Okay, so I can definitely see the problem space. Search API indexes things and if you build a view that shows indexed fields, you're not going to run an entity query on e.g. nodes, thus not allowing Group to kick in.
I just don't think the current approach is something I'd want to maintain. I'm trying to get rid of node grants in core, so why would I add a similar system to my own module? :)
IIRC, search API runs a query once it has its results. I'd love to explore whether we can add onto that query somehow so that the tag that allows group query access to work can be added. But then you could argue whether search_api might be the better place for this to live in, as all access modules could benefit from an 'entity_query' tag or something being added to said search_api query.
Could you reach out @drunkenmonkey and ask for advice? I feel like we need to explore our options here before we can commit to an approach. It's good that you reached out to me so that no more effort went into the current approach before we exhausted all other options.
Thanks!
Used AI to assist with the tedious bits. It got a few things wrong, which I manually corrected.
kristiaanvandeneynde → created an issue.
6 minutes 37 seconds vs 17 minutes 59 seconds. I guess that's faster :)
Fixing a line ending and committing.
kristiaanvandeneynde → created an issue.
Looks good to me.
Only nitpick I have is that the time limit is allotted for every index if you specify no index. You might want to document that as such. If you have 6 indexes and specify a time limit of 10 seconds, it will end up running for 60 seconds unless you manually specify a single index.
We're not using it with a database backend, if that helps. We're using it with www.drupal.org/project/search_api_opensearch
The MemoryCacheInterface is certainly weird and wrong. I think we should require that caches are explicitly specified with an attribute when using autowire. Changing it seems harder than just deprecating it. Lets do that and see if that causes any issues with autowired injections?
+1, we should probably not alias cache interfaces for autowiring as too many things can implement the same interface.
You said not a "real" cache, I think that's not a very meaningful description.
I agree, yet it's the distinction we had to make when we made MemoryCache more widely available and why they are tagged as cache.bin.memory rather than cache.bin. For all I care and for how we use them, they are caches and they replace static property caching perfectly. It's just that they don't fully behave as the other caches in core and, for that reason alone, we have to keep the distinction clear to avoid confusion and mistakes when people try to use them.
Looking at core, the only use seems to be PermissionsHashGenerator and we can just switch that over and get a tiny improvement out of that. Therefore, I'm considering to deprecate cache.static here.
Tentative +1, could be a pain in the ass for tests.
MR looks good to me, although we currently alias
Drupal\Core\Cache\MemoryCache\MemoryCacheInterface: '@entity.memory_cache'
for autowiring, but people might expect that to point to the new cache.memory from the MR.
Also @catch had a point with:
We just need a convention/pattern for cache IDs.
So far, in core, we have:
- cache.jsonapi_memory
- entity.memory_cache
I know Group has cache.group_memberships_memory
So there's no clear naming pattern, but we should ideally have one to indicate the different between regular caches and memory caches. Keep in mind that memory caches aren't real caches because they return the same object over and over again rather than new ones on every get.
If someone has an access policy with the user.permissions cache context, then the whole thing would explode.
/**
* Defines the access policy interface.
*
* ...
*
* Do NOT use any cache context that relies on calculated permissions in any of
* the calculations as you will end up in an infinite loop. E.g.: The cache
* context "user.permissions" relies on your calculated Drupal permissions.
*/
interface AccessPolicyInterface {
:)
Indeed it is :)
Whoops, forgot to refresh before posting, but keeping at NW for the test fails and my suggestion.
I love the language thing in the MR. That's definitely something we could keep. Perhaps we could add a method on the base class so that all access policies can benefit from this? Any access policy that adds a dependency might get bitten by this for no reason.
As far as the user cache tag goes, I suppose that's a clever way to optimize things. But there are many a thing related to user entities that could affect access policy outcomes.
E.g.: An external access provider is given your user's e-mail address to determine access. Throughout a request this value changes, but your user was never updated, so the cache tag was not invalidated. Now what?
So on the one hand I also love that change and the performance improvement that comes with it, on the other hand I feel like we need to consider that one a bit more carefully than what you did with the language cache context.
Similarly, this isn't about old and new VariationCache implementation. It's about now actually using it for the static cache where previously we had nothing like that. VariationCache performance was never great, but it's less of an issue when it's used for a persistent cache that's much slower and less frequently used. 150 calls and 15% of total execution time is massive and imho a major performance issue (with the caveat that it's maybe only 10% or even 5% without blackfire, very hard to tell, but it's definitely something)
This is true, we never really had the chance to statically cache variations and now we do. Technically speaking it's the only safe way to statically cache something that came from a variation cache, though, as we discussed on Slack.
We discussed this quite a bit in https://drupal.slack.com/archives/C1BMUQ9U6/p1756840947127769, and the conclusion is essentially that using a simple static cache here is a possible source for access issues if the policies can vary for the same using during the same request in a way that is not covered by cache tags (e.g. user or role save). Given that this is a possibility, we shouldn't have non-variation static caching on top of a variation static cache. Of course just removing that would make this issue even worse (and add 50% more calls to processAccessPolicies(), so we can realistically only do that if we also come up with a massive performance improvement for that.
Great summary and fully agree.
Improve the performance of VariationCache in general, specifically the cache id calculation. Not sure how.
I thought about this too, one of the key bottlenecks is that we have to go to cache contexts all the time while following a redirect chain. Cache contexts are supposed to be fast, but that is not always the case. Now we can't outright cache cache context values for the entire request, but perhaps we could while following a redirect chain?
I.e.: If we are following a chain that early on uses cache context "foo" and said context is quite expensive, perhaps we could somehow cache the value of "foo" throughout the creation of a redirect chain and make the CacheContextManager aware of that value?
This seems entirely safe to do, as one could argue a cache context changing value while you're following a redirect chain would be really bad as the value to be cached is no longer represented by its eventual cache ID. E.g.: The UserRolesAccessPolicy calculating one thing and before said calculation gets cached, the roles have changed. That would mean someone deliberately saved roles from within another access policy, which is really bad and should not happen.
So, again, it would be safe to say that, as you're building a redirect chain, you should keep using the same cache context values until the end of the chain is reached.
I feel like this could be really promising to explore.
Figure out a rule on when we can skip the variation cache in favor of a simpler per-user static cache. Essentially something like \Drupal\Core\Session\AccessPolicyProcessor::needsPersistentCache() but for variation caching. We'd ask access policies whether they are free from outside/dynamic influences. Given the same user, it will return the same user. SuperUserPolicy is very obvious, UserRolesAccessPolicy as long as the user or roles don't change, which is mostly covered by cache tags.
Worth investigating, but might run into issues where it's hard to predict outside influences.
But by default (with just core), we don't even do persistent caching anymore. For the static cache, I think that's the wrong goal. Multiple users in the same request is an edge case, we shouldn't optimize for the edge case.
So if we add the uid to the cache keys, at least for static cache, then we can drop all user.xy cache contexts. Only problem I can think of right now is that you could alter roles on a user entity without saving that user (saving we could handle with user:ID cache tag), but I don't think that's something we need to support?
Not entirely sure I can follow here. I agree with the things you say we shouldn't support, but I don't fully see how this would be a solution.
That still leaves us with cache redirects. Because the other reason that this is so slow is having to do all of this multiple times to resolve redirects. On multilingual sites, you always end up with languages:language_interface. which is irrelevant for permissions/access, but the user label has possibly been translated. That's the risk of addCacheableDependency(), you often get stuff that you don't need and it's tricky to know what you really need. we should either switch to ->addCacheTags($user_role->getCacheTags()) in UserRolesAccessPolicy or explicitly filter that out.
If we optimize cache redirect following with my suggestion above, we might not need to filter out anything. Each VC lookup will go to every involved cache context only once then, regardless of how long the chain is.
That might be safer, in theory you could have something like per-domain user permission overrides, although thinking about that hurts my brain.
This is actually not that hard to pull off and a valid use case I've seen implemented using the access policy API.
Just ran into this bug myself using the entity_reference_revisions module.
If you have a base field called "category" and use a field type of "entity_reference", then the shared table will only contain one column called "category" and any entity query with a condition of "category.entity.name" will work because of the current code in Tables using the chunks as a guide to the column name.
If you use an entity_reference_revisions field, your shared table will have two columns: category__target_id
and category__target_revision_id
. Now the code described above will try to find a field named "category" because it's parts of the chunks and fail.
However, any entity query that simply queries for "category" will work, because Tables has separate code for that:
// If there are more specifiers, get the right sql column name if the
// next one is a column of this field.
if ($key < $count) {
// THIS BROKEN CODE DOES NOT RUN IF ONLY ONE CHUNK
}
// If there are no additional specifiers but the field has a main
// property, use that to look up the column name.
elseif ($field_storage && $column) {
// THIS CODE WILL CORRECTLY FETCH THE RIGHT COLUMN NAME
$columns = $field_storage->getColumns();
if (isset($columns[$column])) {
$sql_column = $table_mapping->getFieldColumnName($field_storage, $column);
}
}
The ERR module's queue has a few issues complaining about this bug, but none of them seem to correctly point to this core issue.
This is the correct core issue: 🐛 Entity field relationship queries for multi column field items stored in shared tables are broken Needs work
Not adding new test coverage, but I fixed the existing tests. Especially the missing schema would make this patch throw exceptions when used on an actual project.
kristiaanvandeneynde → made their first commit to this issue’s fork.
Found a minor issue when passing a time limit of 0. The code seems to specifically allow for this, but then any time limit of 0 is converted into -1.
Need more time to respond properly, but just to clarify ::get*Persistent*CacheContexts() has nothing to do with the type of cache. It means that these cache contexts are always there for that access policy, with more cache contexts optionally being added during its calculatePermissions() or alterPermissions() methods.
It's also often referred to as initial cache contexts, but that implies you need to add more, so I/we chose getPersistentCacheContexts as a name.
As to the IS and the proposed solution: I agree that if we use a static cache for the hash, we could just as well use one for the cacheable metadata as both rely on the same source. So if we were to go with that approach, consider me on board.
What @berdir wrote in #5 is also true.
A few things I slightly disagree with, though:
As I've mentioned in the redirect issue, the flexibility we gained (which core by default doesn't use) comes at a high price.
There were several things in core that were truly broken with the old redirect system because it combined all possible variations on the second level. This meant you could not use highly variable cache contexts on common page elements such as local tasks because it would make every page uncacheable. With VariationCache, this is no longer an issue.
VariationCache is very slow
Performance tests at the time showed that VariationCache wasn't necessarily slower than the old system either. Depending on the scenario it was even faster.
I wonder if we can add one more check like \Drupal\Core\Session\AccessPolicyProcessor::needsPersistentCache() and somehow avoid the variationCache here when we don't expect anything to actually *vary*.
Worth exploring, but it speaks for itself that we need to tread very carefully there. If we make a mistake in VC while trying to save resources, we might introduce all sorts of security issues.
Would it maybe make more sense to cache the processAccessPolicies() call if we decide it's worth it? createFromObject() should be fast and then we actually reduce 2 calls down further to just one?
The access policy processor has to check at least two static caches to return your access policies: A regular one for cache contexts and a variation cache for the actual result. So if we can turn that into one that would indeed be a win. The question becomes how fast the hashing is vs the static variation cache lookup fewer.
But at that point we'd be putting a regular static cache in front of the caches in AccessPolicyProcessor just to bypass the static variation cache lookup in APP. If we're going to go ahead and do that, we may as well do the same in the PermissionChecker and further reduce the amount of calls to APP. As Berdir's findings shows that PermissionChecker is the other major driver of traffic towards APP.
This does mean that we'd be introducing an extra regular static cache lookup we know will miss for every first permission check on a request.
There we go, easy fix and confirmed to work on my end. Let's see what tests have to say. Also might need a test to confirm it works and keeps working.
Just got bitten by this too.
Had a few manually crafted links (global text rewritten as link). It works while showing the links in the view directly, but when you hide them and put them in the dropbutton, they lose the first part of their path because of the code linked in #5. The hard-coded forward slash gets prefixed to the one coming from the link, leading to an internal url like internal://node/1
, which makes pares_url() think the host is "node" and the path is "/1"
Not sure how you could consider this working as designed, though :) At least we could check if $path starts with a forward slash and strip that before feeding it into UrlObject::fromUri('internal:/' . $path)
?
Then I type "gr us" or "gro us" or whatever :) PhpStorm is really good at autocompleting for you.
Moving back to normal as I found out Search API will always convert whatever data that came from the index into arrays. Which raises the question whether we need to even bother storing the data as scalars.
The field we were struggling with was being added by a views data alter gone rogue.
I just did some more digging and this cannot be solved in only this module. I implemented IndexParamsEvent to make sure my index had actual scalars in it and verified that using the opensearch dashboard.
But Search API still uses arrays as ResultRow properties, because of SearchAPiQuery::addResults() relying on a bunch of Drupal\search_api\Item\ItemInterface
, which in turn contain a bunch of \Drupal\search_api\Item\FieldInterface
. The latter can only contain array values and this module's QueryResultParser::parseResult()
correctly feeds it that.
So even if we store scalar data in the index, Search API will still pass it around as arrays once retrieved from the index. Which means the problem from my previous comment persists.
I just ran into this as my view was breaking.
What happens is that, if you configure a view for an index named Foo, to use the field "Link to InsertEntityTypeName" under the category "Index Foo", the view crashes.
This is because said field will eventually end up calling for the "langcode" property of the row in Drupal\views\Entity\Render\TranslationLanguageRenderer::getLangcode()
with a $this->langcodeAlias
as "langcode"
/**
* {@inheritdoc}
*/
public function getLangcode(ResultRow $row) {
return $row->{$this->langcodeAlias} ?? $this->languageManager->getDefaultLanguage()->getId();
}
Because $row->langcode
is an array containing a single langcode rather than a string, the parent call from \Drupal\views\Entity\Render\EntityTranslationRendererBase::getLangcodeByRelationship()
crashes because it has a return value type of string.
public function getLangcodeByRelationship(ResultRow $row, string $relationship): string {
Fields from the category "Foo datasource" do not seem to suffer from this as they use search_api's EntityTranslationRenderer instead. But that's from my limited testing.
Long story short:
- Views expects strings in some places
- This module returns arrays of strings
- Weird crashes and painful debugging ensue
So I'd bump this to major given how it can completely break Views :)
Add a couple functions to the AccessPolicyInterface that requires the policy to return a list of the affected permissions
That might give false info as another policy could easily override that.
A description could be nice. So maybe start with an overview of currently active policies that show a name and description and then instruct the more technical people where to look if they want to see the code?
Either way, I'm a fan of an overview. I just don't want to overload access policies with methods for the sake of the overview; getLabel() and getDescription() could be nice, getAffectedPermissions() not so much.
You could use attributes, but those get discovered regardless of whether the policy is actually in use. E.g.: If module A has an access policy that module B swaps out, then there's no point in showing module A's version in the UI. With attributes, you would do just that, whereas with methods you would only load the active policies. (Swapping out or decorating access policies is an edge case and generally not recommended, but it's nice to keep the option open)
Moshe suggested you try putting this in https://www.drupal.org/project/security_review → . You could start by listing the class names in your UI, see how that looks and then circle back here to request two new methods for more descriptive info
Attached MR doesn't fix anything, it merely reverts to calling the parent so we have a patch to use while otherwise using the latest version of WSE. Waiting for maintainer response to decide a proper fix before doing any real work.
kristiaanvandeneynde → created an issue.
kristiaanvandeneynde → changed the visibility of the branch bad_di_fix_with_bc to hidden.
I was asking myself: "Why isn't this using setter injection?" when I noticed this issue and saw that @godotislate did exactly that. So huge +1 for MR 69 as it's both forward and backward compatible. We should not be juggling with version_compare for this (MR 68).
I'm unsure where the notion of of custom $ops comes from exactly, do we have any data that supports that this is a thing people did?
No, and people shouldn't have. I'm trying to predict possible fallout from the changes. Like how we sometimes go the extra mile even though our BC policy says we are free to just change constructor arguments on some services: It's being nice. But I doubt we need to do that here.
The idea is that we isolate the code in a way that allows us to provide an alternative implementation using the new mail API, first in an experimental module, then later moved back to user (at least that's my recommendation).
Yeah, I got that and I like the idea. My suggestion was more that, when copying the old code into the (perhaps temporary) service, we might want to see if the comments are still up to standards. But if we want to keep the scope narrow, I'd be fine with that too. As you indicated: This code might not live very long.
My main concern was that someone who used to use custom OPs would now have that option taken away from them. But giving it some more thought, nothing stops them from calling the mail service with said custom OP themself. So I left a few nits left and right, but nothing too major.
The new service copies the old code in ways that seem to make sense, but also do not go any further. One could argue now would be a good time to revisit some of the comments, but if you want to limit scope to "just move the code" then I could get onboard with that too.
Moshe seemed okay with the approach taken here too (via Slack) and so am I. So as far as subsystem maintainers are concerned, you are 2 for 2. Thanks for working on this.
I just realized we're not taking list cache tags into account here. This could become a security issue if released like this. So we need to create a follow-up that makes sure we tackle list cache tag invalidation too. The only downside is that core makes EntityBase::getListCacheTagsToInvalidate() protected. Would have been really nice to be able to just call that.
One thing I just realized by reading this IS, is that 🐛 Node update hook is triggered upon group content create RTBC did not add list cache tags to its solution.
So if you had a list of 5 nodes you had access to due to how they were grouped and you lose that access, we are fine because the list was tagged with both the individual nodes' cache tags and node_list. However, if you gain access because a node got added to one of your groups, the list won't show it because we no longer invalidate list cache tags.
Now, to the issue at hand, hook_entity_access() does not apply to lists, so the facepalm I just had should not get worse if we do as the IS suggests. And as you mentioned, adding or removing an entity from a group will either save the entity (v2/v3) or invalidate its tags (v4). So a main menu block should always get updated when one of the nodes that are actually in the menu have their group status changed.
So I think you're right and we can completely remove that piece of code.
Fixed for v3, on to v2.
Okay, time to backport this.
https://github.com/sebastianbergmann/phpunit/pull/5225
Essentially, the following is no longer supported (see second note):
* @depends testRevisionsTab
* @dataProvider revisionsOperationsProvider
Unless we stop using named arguments in our cases.
Okay turns out there's more to it than simply updating the version. Let's do that in this issue.
Thinking of lifting the new editor flow where we use contexts into a minor release. Already getting rid of the wizard in the issue about not allowing automatic memberships outside of the form approach so that's one step in the right direction. Then 4.1 could contain more context provider support.
Good catch, can definitely accommodate this.
kristiaanvandeneynde → created an issue.
Also, will fix composer in a standalone issue on Monday. Don't need tests to run just yet anyway.
Okay so I did some debugging and found that during an access plugin's lifespan, the view is far from initialized as it saves a lot of resources to not calculate or render a view you don't have access to. But we do need some of the view to be loaded to know which contextual filters were configured.
The latest commits do just that: If, and only if, the GroupPermission plugin is configured to also check the view argument will we load the handlers for the provided display ID and see if any of them use our GroupId ViewsArgument plugin.
This all relies on $this->view->element being populated, which is usually the case when access() is called. But I have a feeling if anything about this approach is fragile, it's that little part. So curious to see what VBO does with $this->view->element, for instance.
Either way, needs an update hook to fix our default views to actually use the group_id
plugin ID instead of numeric
.
Okay, so I gave this some more thought and I want to document some findings for posterity:
Q: If we're going to allow the GroupPermission access plugin to use the value from the argument (contextual filter), then why don't we put the new stuff where you choose a context provider inside the views argument? Then we can convert the access plugin to always use the argument instead.
A: Because the group won't always be in the contextual filters. Imagine a scenario where you can publicly view articles, but if you're a member of whichever group the article belongs to, you can also see whatever annotations the editors made in a Views block in the sidebar. Here, the contextual filter would be the node ID, but the GroupPermission plugin would have to be configured with the "view annotations" group permission on the group provided by a custom context provider ArticleParentProvider or whatever.
So for that reason I've chosen to keep the work I did and combine it with the work from #37, as previously explained in #61.
So, even though nothing changes, I'm writing this down in case other people have the same idea and wonder why I didn't offload the GroupPermission plugin's group negotiation to the views argument entirely.
@graber and I were talking on Slack and I think we can incorporate the patch from #37 into the current MR.
Let's say you have a view where you want one (or a few) context provide(s) to be checked, but on certain pages you want said view to be used inside a block with a hard-coded group ID as the view argument. It would make sense that the view argument were evaluated before the context providers then.
So with that in mind, we should add a checkbox on the config form allowing people to opt into this behavior and explaining that, when checked, a manually provided view argument will always take precedence over the context providers. I'll try and work on that tomorrow.
It also occurred to me that, once this lands, we should also open up a follow-up to allow the same context provider selection in the contextual filter modal. It makes very little sense to be able to choose which context provider to use for the access check, but not the actual views argument.
Okay, works for both entities and groups now. Also tested with all relationship fields hidden.
So this needs:
- Tests
- Option from group type to "use wizard" removed + update hook
- Option from relation type to "use wizard" removed + update hook
But so far it's looking very good and the code is a lot cleaner this way. No more temp store and wizard stuff.
Yeah that's what I also reckoned from the information I could find. I just felt I'd raise it in case my inexperience gave me false confidence and there was, in fact, an issue with the MR.
Left a comment. Mind you I am very new to fibers, but from what I understand we are manipulating fibers that we didn't start and that seems like it might cause a deadlock if the code that spawned said fiber isn't sturdy enough to deal with our suspension.
Going to leave at RTBC, but would be nice if my mind were put at ease :)
Ah yes, I was unaware of that feature. Thanks!
Okay the PoC now works for group forms.
Still need to check a bunch of stuff, but the gist of it is that whatever is configured in the relationship's form display will be shown inline in the group form. This gives some level of control over the whole thing. Probably should also hard-code language to whatever the parent form specified.
This does mean that any hook_form_FORM_ID_alter for the group relationship form will not apply here. That's an unfortunate drawback.
Thanks! For future reference, where can I find this output myself?
No wait, that seems to be a comment indicating what commands are being run. In that case I'm clueless as to why the pipeline is failing.
kristiaanvandeneynde → created an issue.
🎉
Big thanks to everyone involved!
As mentioned in the other issue, it needs to be a standalone thing. We cannot privilege Pathauto over other contrib modules so it's best to leave any contrib module related code out of Group and put it into targeted submodules instead.
Follow-up for Pathauto here: ✨ Consider a submodule to support pathauto Active
kristiaanvandeneynde → created an issue.
Okay CI is still running 11.1, let's not have that stop us as the 4.x branch will only support 11.2 and up anyway.
Closing as a duplicate of the other issue as that one is about to get merged.
CR looks nice, MR looks nice. Running tests once more now that Drupal 10.2 is officially out. If all is well, I will merge this in.
Assigning credit already.
Let me put it differently: If I were using a contrib module that extended a @final class and after a while my site broke out of the blue I'd be pissed. But if said module could not be installed without patching core (until the core issue re final is resolved), at least I'd know exactly what I'd be getting myself into.
1 if contrib needs something that is final they are cannot, contrib cannot require projects using them to patch core.
Then they would have a valid case to open a core issue asking to not mark said class as final and we get to my point about a discussion taking place: "Why does your module need to extend a class that we don't wish to offer support for?" If it's a good case, we can consider removing final or opening up our code for better alterability.
2 ghost wasn't blocked by drupal using final but symfony
What was the outcome of the issue he opened in the Symfony issue queue?
The other big issue with final is it adds more examples of open source preventing people from using open source code.
To counter with a bit of an exaggeration myself: I could mark half the classes in my module as final and people would still be able to use it just fine.
Why does glass have to be broken. We don't even put @internal in all internal classes.
But @final =/= @internal?
For internal code it could very well be that we allow internal extensions of it, but still do not wish to offer public API support for it. The difference is that we do not have a language construct to enforce that so we fall back to the second best thing. For final code we do have a construct, so that should be the default.
Again, if the decision falls to use @final I will follow it, but so far I haven't seen that many strong arguments for it. Both @final and final would have the same implications across the board, except one requires more deliberate action to get around. I find that virtual "I hereby acknowledge I'm about to fuck shit up" checkbox quite useful.
Unless anyone has any further objections, I'm thinking of committing this this week.
How does final help this situation? You can still miss the final unless you mean the suggestion includes code, but then you just can't even try! final prevents all extension. Are you talking about in contrib? Again, if marked final you can't even work around it. If the contrib maintainer would close an issue because it extended @final code they surely wouldn't accept code that would require patching out final. If I'm misunderstanding something please clarify.
If you miss @final
, nothing will stop you from extending the class. If you miss final
, PHP will stop you.
But my point wasn't necessarily about people making mistakes, it was about showing intent and requiring intent when people do want to break glass.
There's a few cases where we might want to use some concept of final. This issue, right now, isn't about when that is but what we can do when it is the case. Although the scope has changed a lot already. So with that scope in mind, there's only a couple of situations where we might encounter final:
- In contrib
- In core
- In one of our dependencies
For both 1. and .2 my comment from #67 still stands: If you have a project where you want to extend a final class and fully well know that it's an edge case or project-specific requirement, by all means write a local patch that merely removes the final keyword and be done with it. Intent is clear, and you have made a manual, small effort to break glass and know you're on your own from here on out.
For 3. I would expect core/contrib not do anything funky there other than perhaps decorate the class, but according to #70 that expectation might be wrong?
Now, for the elephant in the room: @ghost of drupal past's effort to make entities use DI. Here you obviously wouldn't write a local patch, but instead go to the issue queue and argue your case that you are indeed blocked by the final keyword and think you could make a great contribution if it were removed.
At worst, it starts a discussion on why that piece was marked final, you get more info and nothing happens. At best, the final keyword is removed or the final class is refactored to allow for outside influence without extending said class.
Either way, I don't think there are that many efforts where a big change is blocked by the final keyword compared to the amount of times people are deliberately breaking glass, knowing what they're doing is risky business and accepting said risk. So I can see the amount of local patches far exceeding the amount of times we'd have to open an issue to discuss the removal of final
somewhere.
So I'm erring on the side of final over @final again because in most cases it requires the person breaking glass to show intent, instantly making it clear to everyone on their project that something funky was done and support was forfeited.
One thing that's starting to bother me about the terminology of "break glass when in need" is that we're not actually making people brake any glass :)
Unlike the final keyword where you really can't extend it or get errors, the @final annotation only warns you in your IDE and some inspections but does not stop you from extending at all. So it's more like a "Do not trespass" sign on a bank vault that's wide open. It doesn't require much effort to break into said vault.
This leads me to reconsider the benefit of @final. One could argue that "breaking glass" would be writing a small patch that removes the final keyword from whatever core or contrib class you wish to extend. At that point it's both a deliberate action and plainly obvious from the patches folder that someone did, in fact, break glass.
I'm afraid that allowing people to extend @final classes at their own risk will cause confusion among people brought in to debug why their project is acting so weird. It won't be that obvious from the code base that a final class was extended.
Either way, just my 2c. I still stand by #35: If the majority is in favor of @final, I won't go lying on the train tracks so to speak. But now that I've given it some more thought I'm starting to lean towards the keyword again. We're simply not allowing people to "break glass" when in need, IMO we're just removing the glass altogether.
Oh yeah, we can provide whatever we want. That "whatever" should be part of this MR, though, so that we can consider the reported issue here fixed.
Either way, attached screenshot shows the progress I've made today. Seems to work rather well, but still need to write a CR and put the proper link in there. Also, tests would be nice.
Okay so the groundwork seems to be working. Great.
An even better approach would be to allow a list of context providers to be selected that will run in the specified order until a group is found. Then you can reuse "Group from URL" without having to copy its code into "Group from VBO". It would lead to less copy-pasted code all around.
If we switch to that, then I could write a small support module that contains the code from #16 and people could configure their permission plugin to use "Group from URL", followed by "Group from VBO" if they so choose.
I'll try that next. It would fully solve this issue, open up Group's view plugins to work with contexts and make any future request far easier to fulfill "from the outside".
I have plans for Group 4 to rely more on contexts. Group Sites already heavily does and it's been working great there. So I'd like to explore an option more in line with that to make everything work more nicely together in future versions.
The patch from #37 is guesswork. As soon as someone comes along with different needs, we're right back to square one on this discussion. I'd like to fix this in a way that I can tell anyone with slightly different requirements to "just write a context provider" that contains their requirements. Then we can close this once and for all.
Do you want your views to work with Group Sites, which detects the "active group" from domain, path, or whatever you want? Great they will work with that. Do you want them to work with VBO instead? Also possible...
I'm trying to see how this approach is inferior to or less flexible than the patch in #37. Could you please provide more detail? Am I missing something?
Okay so I've given this some more thought and I don't see why a context provider wouldn't work. By default we can use "Group from URL", which behaves 100% like the current code does. With all the same upsides and downsides.
But now you could write your own context provider that defaults to "Group from URL", but falls back to whatever you like. For example, you could first check the route and, in absence of a group entity, see if VBO is installed and try to get the group from the information in the temp store, like @larowlan suggested in #17.
Re #49:
context - based access is something that should be avoided as it creates ways to bypass restrictions
The access wouldn't be based on the context, it would still be based on whether you have the right permission in a Group entity. All that changes is that you have more power to tell Drupal which Group entity you want the permission to be checked against.
- an individual either has access or doesn't and not maybe has and maybe not, so site builders can accidentally leave some doors open and create security issues.
Not sure I can fully follow here. Isn't it always the case that site builders can misconfigure their site if not careful?
Also, it seems bad that group prevents access to routes without group context as usually in cases outside of some module scope neutral would be returned. Can we just return TRUE if no context is available?
Sadly no, because that would actually increase the risk of showing data that someone shouldn't see.
Now I'll admit that in most cases you could argue that, if the route does not contain a Group entity, the view will probably show nothing and then we could allow access. But as demonstrated in the VBO scenario, sometimes the route doesn't know which Group to deal with even though it should. In that case it seems far safer to block anything from happening further than allow perhaps unpredictable code to run. I would imagine that creating a bunch of group relationships without a group to point to can lead to crashes.
Either way, I'll pick this up today and see what I can come up with. I'm very much a fan of a solution which takes guessing completely out of Group and into the responsibility of those who have certainty about their environment. Be it the VBO maintainers, myself (with a VBO support submodule) or developers working on a specific client project with custom Group detection logic.
Yeah I'd need to look into this but when I added it way back when there was no core-provided alternative. We could compare the one from core to the one from Group and perhaps remove the one in Group. Would need an update hook to convert all blocks to start using the one in core.
Big RTBC+1.
To go into a little more detail: At first the processor was designed as a chain processor, which itself could be put into a chain. This design was copied from similar service collectors, but abandoned during the review process of getting Flexible Permissions into core as the Access Policy API.
The service alias must have gotten through the cracks of code review and it surprises me it took this long for someone to find it :) Once again, good catch. This should be easy to commit.
This is indeed an oversight when converting from Flexible Permissions. Fix is straightforward, but tests seem to be failing on Nightwatch, please rerun those. And this should indeed be backported to D10.
Thanks for finding this!
Added code that also invalidates cache tags when an entity is removed from a group and expanded the test case.