Problem/Motivation

When entity operations are rendered (particularly in views), there is no way to add cacheable metadata to the operations, therefore operations output is unable to be varied by any cache contexts.

This is a major issue when using things like "edit own" permissions, or the Workbench access module. These can both vary edit access to an entity based on the user, which means without cacheability users will see operations that they don't have access to.

Steps to reproduce

  • Install Standard
  • Add a role X that has edit own article content and access content overview permissions
  • Add 2 users User A and User B that both have Role X.
  • As an admin user, create two Article nodes, and make User A the author of one node, and User B the author of the other node.
  • Go to admin/content
  • When Admin is logged in, the Edit operation link is available in the view for both nodes (which is the expected behavior).
  • When User A logs in, and the Edit link is available in the view only for the node that has User A as author (which is the expected behavior).
  • When User B logs in, and the Edit link is available in the view only for the node that has User A as author, when it should be for User B as author.

Taken from #2653690: Operations links field in Views fails between users with "edit own" user permissions

Proposed resolution

Add a new $cacheability parameter to the following locations:
- \Drupal\Core\Entity\EntityListBuilder::getOperations()
- \Drupal\Core\Entity\EntityListBuilder::getDefaultOperations()
- hook_entity_operation
- hook_entity_operation_alter

The hooks can have new parameters added safely without BC concerns. The EntityListBuilder functions will have new commented out arguments to utilise the Symfony debug class loader

Remaining tasks

Review https://git.drupalcode.org/project/drupal/-/merge_requests/12445

User interface changes

N/A

Introduced terminology

N/A

API changes

New $cacheability parameter in the following locations:
- \Drupal\Core\Entity\EntityListBuilder::getOperations()
- \Drupal\Core\Entity\EntityListBuilder::getDefaultOperations()
- hook_entity_operation
- hook_entity_operation_alter

Data model changes

N/A

Release notes snippet

N/A

CommentFileSizeAuthor
#164 2473873-164-current-mr-12445.patch29.92 KBthefancywizard
#150 2473873-150-reroll.patch26.97 KBthefancywizard
#135 2473873-134-reroll.patch33.98 KBlduong
#133 2473873-D10.3.x-2024-09-25.diff35.16 KBgaëlg
#129 2473873-126-reroll.patch25.12 KBmanthan.chauhan
#126 2473873-126.patch26.38 KBbircher
#122 drupal-views-entity-operations-caching-3411947-3.patch26.41 KBthefancywizard
#112 2473873-nr-bot.txt90 bytesneeds-review-queue-bot
#103 2473873-nr-bot.txt90 bytesneeds-review-queue-bot
#97 2473873-97.patch10.4 KBkostyashupenko
#94 interdiff-81-94.patch9.29 KBsimeonkesmev
#94 2473873-fix-entity-operations-cacheability-8.9-94.patch10.52 KBsimeonkesmev
#94 2473873-fix-entity-operations-cacheability-94.patch10.4 KBsimeonkesmev
#92 2473873-83-8.9.patch10.52 KBsimeonkesmev
#92 2473873-83.patch10.4 KBsimeonkesmev
#91 2473873-83.patch9.08 KBsimeonkesmev
#90 2473873-83-8.9.patch9.2 KBsimeonkesmev
#90 2473873-83.patch9.08 KBsimeonkesmev
#88 2473873-82.patch9.08 KBsimeonkesmev
#88 2473873-82-8.9.patch9.2 KBsimeonkesmev
#81 2473873-interdiff-80-81.txt1.99 KBcburschka
#81 2473873-81.patch8.68 KBcburschka
#80 2473873-interdiff-75-80.txt1.25 KBcburschka
#80 2473873-80.patch7.81 KBcburschka
#78 interdiff_75-77.txt1.58 KBshaktik
#78 2473873-77.patch7.41 KBshaktik
#76 interdiff_70-75.txt2.14 KBshaktik
#75 2473873-75.patch6.46 KBshaktik
#70 2473873-views-entity-operations-cacheability-70-9.0.patch4.31 KBdpi
#70 2473873-views-entity-operations-cacheability-70-9.1.patch4.32 KBdpi
#66 interdiff-2473873-65-66.txt615 bytesacbramley
#66 2473873-66.patch4.33 KBacbramley
#65 interdiff-2473873-63-64.txt719 bytesacbramley
#65 2473873-64.patch4.32 KBacbramley
#63 interdiff-2473873-62-63.txt364 bytesacbramley
#63 2473873-63.patch4.34 KBacbramley
#62 interdiff-2473873-60-62.txt724 bytesacbramley
#62 2473873-62.patch4.11 KBacbramley
#60 2473873-60.patch3.33 KBacbramley
#45 add_cacheablity_support-2473873-45--reroll.patch61.95 KBborisson_
#40 drupal_2473873_40.patch61.94 KBxano
#40 interdiff.txt3.08 KBxano
#38 drupal_2473873_38.patch60.19 KBxano
#38 interdiff.txt2.08 KBxano
#35 drupal_2473873_35.patch43.94 KBxano
#35 interdiff.txt9.9 KBxano
#29 2473873-29-interdiff.txt7.41 KBberdir
#29 2473873-29.patch41.87 KBberdir
#27 2473873-27.patch37.49 KBberdir
#15 2473873-15-interdiff.txt394 bytesberdir
#15 2473873-15.patch40.29 KBberdir
#12 2473873-12-interdiff.txt1.46 KBberdir
#12 2473873-12.patch40.29 KBberdir
#12 2473873-12-tests-only.patch33.26 KBberdir
#5 2473873-5.patch7.42 KBdawehner
#3 2473873-3.patch10.63 KBdawehner
#4 2473873-4.patch5.59 KBdawehner

Issue fork drupal-2473873

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

fabianx’s picture

As spoken in IRC for now we can just do it like NodeViewBuilder and push the operation links (like node links) into a #post_render_cache.

dawehner’s picture

StatusFileSize
new10.63 KB

@plach and myself worked on that a bit, first without #post_render_cache.
While doing so I realized we probably need #2487600: #access should support AccessResultInterface objects or better has to always use it

Now trying to get a #post_render_cache working, which is probably the better solution, given the cacheabilty is probably often not worth to do.

dawehner’s picture

StatusFileSize
new5.59 KB

just temporary work on post_render_callback.

dawehner’s picture

StatusFileSize
new7.42 KB

Worked a bit more on #3 and replaced the entity operation links properly as well with access result bubbleable metadata.
On top of that, it adds support for #access being an AccessResultInterface object.

berdir’s picture

Status: Active » Needs review
Issue tags: +Contributed project blocker, +Needs tests

Tagging this with contributed project blocker... file_entity tests are currently failing because of this.

I've confirmed that this fixes them, sounds like we need tests.

The problem with the test coverage in RowRenderCacheTest is that we have a single view that has separate view, edit, delete link fields *and* the operations link. It's enough for the tests to pass if *one* of them is implemented correctly. So operations look like they work right now, but they don't.. it only works because the other fields are adding the user.permissions cache context it and counts for the whole row.

Sugestion: Split up the test view into 4 different ones, test each field on a separate view.

dawehner’s picture

Sugestion: Split up the test view into 4 different ones, test each field on a separate view.

Sounds like a good idea in general.

The last submitted patch, 3: 2473873-3.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 5: 2473873-5.patch, failed testing.

plach’s picture

Suggestion sounds good, I was already planning to do something like that, just forgot it.

The last submitted patch, 4: 2473873-4.patch, failed testing.

berdir’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
StatusFileSize
new33.26 KB
new40.29 KB
new1.46 KB

Tried that. Splitting up the view into 4 makes the patch/test part pretty big.

The test shows that the title and operations column still dont' have the user.permissions cache context as expected. Need to look into #2335661: Outbound path & route processors must specify cacheability metadata to figure that out for the make_link option (title), didn't look into operations yet.

Also fixed a fun bug in the new access check that resulted in denying access to anything without #access. Result was an empty page for *everything*, confused me for a minute ;)

The last submitted patch, 12: 2473873-12-tests-only.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 12: 2473873-12.patch, failed testing.

berdir’s picture

Status: Needs work » Needs review
StatusFileSize
new40.29 KB
new394 bytes

We need to default link access to TRUE. While that might be uncommon, it matches the current behavior. This will hopefully fix most of the failing tests...

dawehner’s picture

You could also just go with one view and 4 displays, I think this could reduce the patch size quite a bit?

berdir’s picture

Possible, but I'm not exactly sure how to write the test then, as we directly interact with the view object and don't render them in advance. Not quite sure how that would work, is it possible to have the same view with a different active display at the same time? Maybe cloning it and switching?

Status: Needs review » Needs work

The last submitted patch, 15: 2473873-15.patch, failed testing.

dawehner’s picture

Maybe cloning it and switching?

Yeah you should be able to just do:

$view = Views::getView('foo');
$view->setDisplay('page_1');
$view->...

$view = Views::getView('foo');
$view->setDisplay('page_2');
$view->...

$view = Views::getView('foo');
$view->setDisplay('page_3');
$view->...
berdir’s picture

That doesn't fit well with how the test is structured now. We first preview the view(s), and then loop over all nodes and check the different fields. We'd need to change it to do 4/5 loops so that each field + the cache check is in a separate loop.

fabianx’s picture

+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -138,7 +139,10 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
+    if (isset($elements['#access']) && (($elements['#access'] instanceof AccessResultInterface && !$elements['#access']->isAllowed()) || !$elements['#access'])) {
       return '';
     }

The reason this still fails is that we _need_ to return the cacheable metadata when this is an object.

And strictly speaking after this change we need to return max-age = 0 when its not an object, but just 'FALSE'.

wim leers’s picture

I think we want to move the #access portion of this patch be moved to #2487600: #access should support AccessResultInterface objects or better has to always use it?

dawehner’s picture

I think we want to move the #access portion of this patch be moved to #2487600: #access should support access result objects or better has to always use it?

So you basically argue that we should first solve #access in all of core instead of the limited scope needed for entity operations? We could proceed here with just changing the bits which are relevant to this particular issue. How sane would that be?

wim leers’s picture

No, not arguing that. Proceeding here is fine. I think I only meant to say that the #access portion of this issue, which is something we need to fix regardless of this particular issue, probably makes more sense to fix in a separate issue. That's all.

berdir’s picture

Status: Needs work » Needs review
StatusFileSize
new37.49 KB

Reroll, didn't try it at all yet.

Status: Needs review » Needs work

The last submitted patch, 27: 2473873-27.patch, failed testing.

berdir’s picture

Status: Needs work » Needs review
StatusFileSize
new41.87 KB
new7.41 KB

Fixing the tests.

* Fix operations to a) bubble up the cache metadata ( this might need to be fixed for #type links too, we just don't have tests for that) and b) skip inaccessible links completely. The code that was committed to HEAD renders empty list elements, and the RowRenderCacheTest explicitly checks for an empty string.
* Fixed the add menu link operation access
* Fixed test exceptions that are looking for the exact operation arrays
* the title view, despite having a link does not currently check access (as it is a path configuration), so we special case that behavior in the test. This is not the issue to fix that and we're not even sure we want it to be fixed.

Also discussed this with @plach, @dawehner and @xjm in regards to being critical or not and we think it is not, because there is no real security issue, you just might see some links you then don't have access to or you are missing links that you would have access to. So we can remove it from the must list of the cache meta issue.

(This also means I was working on a major issue, but figuring out if it is critical or not was critical :p)

wim leers’s picture

Issue tags: +D8 Accelerate London

Also discussed this with @plach, @dawehner and @xjm in regards to being critical or not and we think it is not, because there is no real security issue, you just might see some links you then don't have access to or you are missing links that you would have access to. So we can remove it from the must list of the cache meta issue.

(This also means I was working on a major issue, but figuring out if it is critical or not was critical :p)

berdir++

Also updated the meta: #2429287-114: [meta] Finalize the cache contexts API & DX/usage, enable a leap forward in performance.

dawehner’s picture

+++ b/core/includes/theme.inc
@@ -599,12 +601,19 @@ function template_preprocess_links(&$variables) {
 
+      // Check access, skip inaccessible links to avoid empty list elements.
+      if ($link['access'] instanceof AccessResultInterface ? !$link['access']->isAllowed() : $link['access'] !== TRUE) {
+        continue;
+      }

+++ b/core/lib/Drupal/Core/Render/Element/Dropbutton.php
@@ -40,6 +43,17 @@ public static function preRenderDropbutton($element) {
+    $cacheable_metadata = CacheableMetadata::createFromRenderArray($element);
+    foreach ($element['#links'] as $key => $link) {
+      // @todo Should this check the dependency or not? Not checking it would
+      //   disable caching.
+      if (isset($link['access']) && $link['access'] instanceof CacheableDependencyInterface) {
+        $cacheable_metadata = $cacheable_metadata->merge(CacheableMetadata::createFromObject($link['access']));
+      }
+

Here we merge the access metadata, but the generic case of template_preprocess_links() doesn't. Would it not make more sense to do it as part of template_preprocess_links()?

  1. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -482,6 +482,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
    +      //debug($elements['#cache']);
    

    I hope we can drop it ...

  2. +++ b/core/modules/views/src/Tests/Plugin/RowRenderCacheTest.php
    @@ -141,34 +144,49 @@ protected function doTestRenderedOutput(AccountInterface $account, $check_cache
    +            $user_context = !$account->hasPermission('edit any test content') ? 'user' : 'user.permissions';
    

    As discussed this is not entirely obvious.

wim leers’s picture

Status: Needs review » Needs work

NW per #31.

wim leers’s picture

Assigned: Unassigned » berdir
dawehner’s picture

Assigned: berdir » Unassigned

Let's whether someone else is motivated to work on this particular issue ...

xano’s picture

Status: Needs work » Needs review
StatusFileSize
new9.9 KB
new43.94 KB

I think we may need to do something like this, because EntityInterface::access() returns AccessResultInterface, which does not extend CacheableDependencyInterface. This also means we're mixing up access and cacheability metadata, which is a smell. Instead, use a cache key for links which contains CacheableDependencyInterface. We can easily generate those from the AccessResult instances which are returned by Entity::access(), and it keeps the API more self-descriptive.

Status: Needs review » Needs work

The last submitted patch, 35: drupal_2473873_35.patch, failed testing.

The last submitted patch, 35: drupal_2473873_35.patch, failed testing.

xano’s picture

Status: Needs work » Needs review
StatusFileSize
new2.08 KB
new60.19 KB

This fixes some test failures, and removes what I believe are redundant cacheability metadata merges.

Status: Needs review » Needs work

The last submitted patch, 38: drupal_2473873_38.patch, failed testing.

xano’s picture

Status: Needs work » Needs review
StatusFileSize
new3.08 KB
new61.94 KB

Status: Needs review » Needs work

The last submitted patch, 40: drupal_2473873_40.patch, failed testing.

The last submitted patch, 38: drupal_2473873_38.patch, failed testing.

The last submitted patch, 40: drupal_2473873_40.patch, failed testing.

xano’s picture

@Wim Leers: This results in per-entity cache tags being added to page cache items. On a list page with 50 entities, that means 50 additional cache tags, and the user context that are added. Is this what we what, or should we lazy-build the operations to keep accurate page output, but prevent ridiculously granular cache items?

borisson_’s picture

Status: Needs work » Needs review
StatusFileSize
new61.95 KB

Patch didn't apply anymore, attached one does.

Status: Needs review » Needs work

The last submitted patch, 45: add_cacheablity_support-2473873-45--reroll.patch, failed testing.

The last submitted patch, 45: add_cacheablity_support-2473873-45--reroll.patch, failed testing.

wim leers’s picture

borisson_’s picture

Issue tags: -Needs reroll

Patch still applies. All the tests are very much broken though.

xjm’s picture

#2530634: Value of last_render_text leaks into the next Dropbutton also sounds related -- the resulting behavior at seems similar, at least, though the title sounds like a different bug.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

xjm’s picture

Title: Add cacheablity support for entity operations » Views entity operations lack cacheability support, resulting in incorrect dropbuttons
Issue tags: +Triaged core major

@tim.plunkett, @dawehner, @alexpott, and I discussed this issue at DrupalCon New Orleans and agreed that it was major. It results in cached administrative views not being properly updated when access changes, which results in both missing links and inaccessible stale links. It is especially confusing because it misleads the user about access to the entities and can result in people doing inadvisable or insecure things to work around the problem.

It is not critical because there is no risk of information disclosure, just missing links or links to 403s (see #29 for earlier discussion of that).

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

acbramley’s picture

Issue tags: +DrupalSouth 2019

Let's try and tackle this at DS2019!

acbramley’s picture

Just a quick review of the last patch to point out some issues

  1. +++ b/core/includes/theme.inc
    @@ -648,12 +651,19 @@ function template_preprocess_links(&$variables) {
    +        continue;
    

    We'll need to add cacheability from a access denied here too. Maybe it's best to add it into $variables['links']?

  2. +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListBuilder.php
    @@ -37,12 +38,15 @@ public function getDefaultOperations(EntityInterface $entity) {
    +      $update_cacheability = CacheableMetadata::createFromObject($update_access);
    

    We have to return this cacheability metadata even when access is denied and bubble it up in template_preprocess_links.

  3. +++ b/core/lib/Drupal/Core/Entity/EntityAccessControlHandler.php
    @@ -82,6 +82,14 @@ public function access(EntityInterface $entity, $operation, $langcode = Language
    +    $return = AccessResult::neutral()->orIf($return);
    +    $return->cachePerUser();
    +    $return->addCacheableDependency($entity);
    

    Not sure what the intention here was but we definitely don't want the user context added to every access call.

I would also suggest we scope this issue to entity operations generated from EntityListBuilder and split the other builder classes out?

acbramley’s picture

Version: 8.6.x-dev » 8.9.x-dev
Status: Needs work » Needs review
StatusFileSize
new3.33 KB

Something like this would work although using array keys like this is pretty ugly IMO, let's see what breaks.

Status: Needs review » Needs work

The last submitted patch, 60: 2473873-60.patch, failed testing. View results

acbramley’s picture

StatusFileSize
new4.11 KB
new724 bytes

We need to add back some of the logic from #45 in template_preprocess_links for lists that aren't using views. This is currently incomplete as it causes empty <li> elements to be rendered.

acbramley’s picture

Status: Needs work » Needs review
StatusFileSize
new4.34 KB
new364 bytes

Woops, missed the use statements!

Status: Needs review » Needs work

The last submitted patch, 63: 2473873-63.patch, failed testing. View results

acbramley’s picture

Status: Needs work » Needs review
StatusFileSize
new4.32 KB
new719 bytes

Having a bad day with this 😅 errors were from undefined index notices.

acbramley’s picture

StatusFileSize
new4.33 KB
new615 bytes

Oh boy...

The last submitted patch, 65: 2473873-64.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 66: 2473873-66.patch, failed testing. View results

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

dpi’s picture

StatusFileSize
new4.32 KB
new4.31 KB

Simple rerolls for 9.1 and 9.0

dpi’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work
shaktik’s picture

Assigned: Unassigned » shaktik
shaktik’s picture

Status: Needs work » Needs review
StatusFileSize
new6.46 KB

Trying to fix the test case.

shaktik’s picture

StatusFileSize
new2.14 KB

Status: Needs review » Needs work

The last submitted patch, 75: 2473873-75.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

shaktik’s picture

Assigned: shaktik » Unassigned
Status: Needs work » Needs review
StatusFileSize
new7.41 KB
new1.58 KB

Fixed all test case #70 patch, Kindly check.

acbramley’s picture

Status: Needs review » Needs work
+++ b/core/modules/views/tests/src/Unit/Plugin/views/field/EntityOperationsUnitTest.php
@@ -130,7 +130,7 @@ public function testRenderWithDestination() {
-    $this->assertSame($expected_build, $build);
+    $this->assertNotSame($expected_build, $build);

@@ -171,7 +171,7 @@ public function testRenderWithoutDestination() {
-    $this->assertSame($expected_build, $build);
+    $this->assertNotSame($expected_build, $build);

That's one way to fix a test haha. I think we need to figure out why these are different and fix $expected_build.

cburschka’s picture

Status: Needs work » Needs review
StatusFileSize
new7.81 KB
new1.25 KB

The test operation has no cache dependencies, so all we need to add to the expected build in order to pass the test is a "permanently cacheable" designation; ie (new CacheableMetadata())->applyTo($expectedBuild);

But maybe we want to expand test coverage by adding (and then expecting) an actual cache dependency to test that it is merged into the render array correctly.

cburschka’s picture

StatusFileSize
new8.68 KB
new1.99 KB

This patch adds a real cache tag to the operation and checks that it passes through into the final render array.

mxr576’s picture

Status: Needs review » Reviewed & tested by the community

LGTM

xjm’s picture

Version: 9.1.x-dev » 8.9.x-dev
Status: Reviewed & tested by the community » Needs review

Great find. I think since this is resolving a major cache invalidation/access bug with no other disruptive changes, it's potentially backportable to 8.9.x.

In addition to the required unit test issues, I'd suggest a functional test for this (maybe a JS test in this case), since the bug is at a functional level. It should theoretically be straightforward to write a test that fails on HEAD and passes when combined with the fix. I'd also want the test to make assertions about the expected access before and after cache invalidation to prove that we're not introducing an access bypass by fixing this. Thoughts?

mxr576’s picture

+1 for Functional test coverage

berdir’s picture

  1. +++ b/core/includes/theme.inc
    @@ -725,6 +727,12 @@ function template_preprocess_links(&$variables) {
           ];
    +      if (isset($link['access'])) {
    +        $link_element['#access'] = $link['access'] instanceof AccessResultInterface ? $link['access']->isAllowed() : $link['access'];
    +      }
    

    if access is an AccessResultObject, we could also consider its cacheability metadata directly, that would make the implementations a lot simpler?

  2. +++ b/core/lib/Drupal/Core/Entity/EntityListBuilder.php
    @@ -128,18 +129,24 @@ public function getOperations(EntityInterface $entity) {
             'weight' => 10,
             'url' => $this->ensureDestination($entity->toUrl('edit-form')),
    +        'access' => $update_access->isAllowed(),
    +        'cache' => CacheableMetadata::createFromObject($update_access),
    
    +++ b/core/modules/config/tests/src/Functional/ConfigEntityListTest.php
    @@ -137,11 +144,16 @@ public function testList() {
    +        'access' => $update_access->isAllowed(),
    +        'cache' => CacheableMetadata::createFromObject($update_access),
    +
           ],
           'delete' => [
             'title' => t('Delete'),
             'weight' => 100,
             'url' => $entity->toUrl('delete-form')->setOption('query', $this->getRedirectDestination()->getAsArray()),
    +        'access' => $delete_access->isAllowed(),
    +        'cache' => CacheableMetadata::createFromObject($delete_access),
           ],
         ];
     
    diff --git a/core/modules/views/src/Plugin/views/field/EntityOperations.php b/core/modules/views/src/Plugin/views/field/EntityOperations.php
    

    Concerned that changing this could break things. In multiple ways.

    One one side, we have the problem that \Drupal\Core\Entity\EntityListBuilder::getOperations() is a public API, it may or may not be used together with #links. Now we expect it to be used with something that respects the new access key. I didn't find anything, so this is possibly unlikely.

    On the other side, we have the problem that there are a lot of custom operations, and as a result of this, we implicitly make them cacheable and might introduce bugs.

    This patch only updates the default method, but there are tons of subclasses implementing this. Just looking at a few:

    \Drupal\filter\FilterFormatListBuilder::getDefaultOperations() checks for the default format, separate config entity.

    \Drupal\Core\Config\Entity\ConfigEntityListBuilder::getDefaultOperations() checks the entity status, so would need top update if the entity changes.

    These and most are config entities, so they are unlikely to be cached, but there's also for example \Drupal\webform\WebformSubmissionListBuilder::getDefaultOperations in contrib, with lots of dynamic conditions.

    This is annoying, I know :)

    One option might be to treat a non-existing cache key as uncacheable, to keep the existing behavior. So every operation needs to opt-in to being cacheable. They could return an empty object to indicate that they are safe, like \Drupal\Core\Cache\Context\CacheContextInterface::getCacheableMetadata.

acbramley’s picture

@Berdir yeah it is annoying, back in #59 I suggested stripping this issue back to just the default class to keep the patch size manageable the idea was to post follow ups to address the other subclasses. Fixing caching bugs always has the opportunity to break things but I'm not really sure what the best solution is here.

I like your idea of returning uncacheable, I'm just not entirely sure what that would look like - maybe I need more coffee first :P

kim.pepper’s picture

Issue tags: +#pnx-sprint
simeonkesmev’s picture

StatusFileSize
new9.2 KB
new9.08 KB

I've made some alterations:
1. Made operations caching per user by default.
2. Simplified the data by adding only 'access' objects to operations that include the caching information in themselves.
3. Made patches for 8.9 and 9.x.x

Status: Needs review » Needs work

The last submitted patch, 88: 2473873-82.patch, failed testing. View results

simeonkesmev’s picture

StatusFileSize
new9.08 KB
new9.2 KB

Here are fixes for the patches. Can't run the full tests locally so fingers crossed this time.

simeonkesmev’s picture

StatusFileSize
new9.08 KB

Mistake in the 9.x.x version of the patch.

simeonkesmev’s picture

StatusFileSize
new10.4 KB
new10.52 KB

Updates of the tests.

vsujeetkumar’s picture

@SimeonKesmev The naming convention of your patch file is incorrect, Commonly we use like issue number and then comment number.
EX: [issue-number]-[comment-number].patch

Also required interdiff, It will help to understand the changes you have done. Create Interdiff

simeonkesmev’s picture

Status: Needs work » Needs review
StatusFileSize
new10.4 KB
new10.52 KB
new9.29 KB

Hi @vsujeetkumar, here's the proper naming and the interdiff.

kristen pol’s picture

Version: 8.9.x-dev » 9.3.x-dev
Issue tags: +DrupalSouth, +Bug Smash Initiative

Thanks for the updates. Patches still both apply cleanly.

I've reviewed the code but this is quite complex so I'll defer to others with more insight there. I didn't notice any nitpicks at least :)

Adding some more tags because this issue really needs more tags </silliness>.

quietone’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests, +Needs reroll

I skimmed the comments and noticed that a Functional test was asked for in #83, setting to NW for that.

The Issue Summary should explain the proposed resolution and list the remaining tasks.

And adding some more tags!

kostyashupenko’s picture

Issue tags: -Needs reroll
StatusFileSize
new10.4 KB

Reroll against 9.3.x is done
For 8.9.x reroll was not needed.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

quietone’s picture

Version: 9.4.x-dev » 10.0.x-dev

I have closed #3012036: Views entity operations field does not contain required cache metadata and #2653690: Operations links field in Views fails between users with "edit own" user permissions as duplicates.

I was testing #2653690: Operations links field in Views fails between users with "edit own" user permissions as part of bug smash triage and was able to confirm the problem as stated in the IS over there is reproducible. I then found this issue and applied the patch and retested. And it fixed the problem. The views for the two users show the operation link on the correct content.

quietone’s picture

Issue summary: View changes

The proposed resolution section of the IS needs to be updated.

acbramley’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -Needs tests

Moving to an MR, rebased against 10.1.x. First pushed a failing functional test and will follow with the rest of the patch. Also updated the IS.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new90 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

acbramley’s picture

Version: 10.0.x-dev » 10.1.x-dev
Status: Needs work » Needs review

Missed changing the version.

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs Review Queue Initiative

Hiding files as fix has been moved to MR 3556
Moving to NW as there appears to be 1 failure in it though.

Did not review yet

twod’s picture

We already have cache metadata for the operations indirectly through their URLs, but most list builders [and the Views Operations field] simply throw it away after evaluating the access condition (at operations build time), so the context of when an operation could be valid or not is lost.

I think this is what the failing test is actually hinting about, a responsibility creep. To fix the failing test I would have assumed the access control handler for the "disable" operation was not returning the proper [user] context. However, adding it there has no effect at all on whether the test passes or fails since that data is thrown away by the list builder when it check if access is currently allowed.
(We try to avoid early rendering, maybe we should also try to avoid early access evaluation in similar situations?)

I ran into this when debugging specifically why our cache contexts added to custom entity access control handlers seemed to have odd effects despite the value of the context(s) we included was different for different users, even after applying the latest patch - because contrib's entity module's list builder has the same flaw as core's list builders currently do.

If the list builders (and Views' Operations field) always added the operations instead of evaluating access at the point of building the operation links, and the links preprocess template hook performs the actual access evaluation (storing the result in $link['#access'] and merging in the cache metadatada into $link['#cache']), we'd be all good.

This would keep access control handlers as the ones responsible for adding the correct access related cache metadata to their responses instead of also having list builders juggle it around, and relieve them of even having to do the operations access checks they do now - they would not have to care about it all about which operations are valid or not.

For now I've had to resort to always include a custom cache context (which varies with anything that could affect access for the different groups of users we have) in the links preprocess hook, otherwise the operations access in the lists/views we have would not get rebuilt at all. If it worked as described above (and entity module always included all of its operations) I would not have had to do anything in our site specific code because the cache metadata from the access handler would have been propagated there instead.

I'll take a stab at refactoring the branch to show what I mean.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

mstrelan made their first commit to this issue’s fork.

twod’s picture

Status: Needs work » Needs review

Now that all tests finally pass, I should write some notes on the changes outside the implementation of #108 itself.

There were at least two major issues discovered while working on this which could potentially be extracted and handled on their own.
I tried searching the issue queue for existing patches and came up blank, but maybe someone knows if they have already been covered elsewhere.

The renderer throws away cache metadata from access results if they are not allowed. This, obviously, prevents something like #108 from working; we need that metadata to be bubbled the same was as if there was a cache hit/miss when access is granted. Without this the new ::testEntityOperationsCacheability() fails because the user cache context is dropped.

+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -233,6 +233,14 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
       if ($elements['#access'] instanceof AccessResultInterface) {
         $this->addCacheableDependency($elements, $elements['#access']);
         if (!$elements['#access']->isAllowed()) {
+          // Abort, but bubble new cache metadata from the access result.
+          $context = $this->getCurrentRenderContext();
+          if (!isset($context)) {
+            throw new \LogicException("Render context is empty, because render() was called outside of a renderRoot() or renderPlain() call. Use renderPlain()/renderRoot() or #lazy_builder/#pre_render instead.");
+          }
+          $context->push(new BubbleableMetadata());
+          $context->update($elements);
+          $context->bubble();
           return '';
         }
       }

(Added null to the annotated return types of ::getCurrentRenderContext() as that was missing.)

The content translation manager sometimes looks for existing translations on the wrong revision.
This was exposed by ContentTranslationRevisionTranslationDeletionTest::testOverview() failing because the 'Add' operation was now missing for Italian on line 142. They still had access to the translate form if the current interface language was either Italian or French, but not in English. While the Add operation [correctly?] points to the Italian version of the translation form it being added depended on an access check performed on the "active" revision - which already has an Italian translation - so the link no longer show up because the URL is inaccessible.

The test performs these steps:
(This is iteration 2 of the test, with the editor role. Iteration 1 passes because access checks are bypassed.)

  1. Create an English published node "Test 2.1 EN"
    Verify translations can be added.
  2. Add an Italian draft "Test 2.2 IT".
    Verify the draft can not be deleted because it's unpublished.
  3. Publish the Italian translation "Test 2.3 IT"
    Verify it can be deleted.
  4. Create an English draft "Test 2.4 EN".
    Verify Italian translation still exists.
  5. Delete the Italian translation.
    Verify the 'Add' operation for Italian reappears. This failed
  6. Publish the English draft "Test 2.6 EN".
    Verify the Italian translation does not reappear.
  7. Add an Italian published translation "Test 2.t IT".
    Verify it can be deleted.
  8. Create an Italian draft "Test 2.8 IT".
    Verify it can not be deleted.
  9. Delete the Italian draft.
    Verify it can be deleted again.
  10. Delete the Italian translation.
    Verify it can be added again.

When the Italian translation is deleted in step 5 the English draft "Test 2.4 EN" is the latest revision and no longer has a translation. The access manager gets its argument from the route match for the translation route, which has load_latest_revision = TRUE, but it loads the latest "active" revision - which the entity repository says is the last one which was translation affected.
The last English revision which was translation affected (titled "Test 2.1 EN") still had "Test 2.3 IT" as a translation, as the deletion created a new revision without any translations affected.
ContentTranslationController::add() deals with this by explicitly loading the latest revision, so we can do the same thing in the translation access control manager and it correctly sees that there is no Italian translation there and allows the 'Add' operation.

The controller does however cause a similar issue elsewhere by adding the translation to the entity it loads (after the access control manager has allowed the route). If you have the language switcher block enabled the links there are now access checked via the same manager, and it now sees the newly added translation (which isn't considered "new" since an Italian translation did exist earlier) and prevents the links from showing.
Adding a clone to the controller prevents polluting the entity instance in the context, and the subsequent access checks then see the true "current" state of the entity and lets the language switcher links show.

Other changes made to make tests pass include adding _access: 'TRUE' to the <current> route, which caused a problem in CommentNewIndicatorTest<code> because <code>CommentLinkBuilder uses <current> for the "comment-new-comments" hidden jump link and renders it as a link list.
I can't think of any reason why this would not be safe off the top of my head, but there's also no other test which directly depends on it it being either allowed or denied.

A few places in FunctionsTest had similar issues with the access checks but some routes could be switched to existing ones with correct access requirements or replaced with a new one.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new90 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

catch’s picture

Splitting those out sounds good and nice one tracking them down!

twod’s picture

Thanks, working on that now.

Do you know what's up with the bot? This is my first time I see it so trying to figure out what it's trying to tell me.
The patch applies fine against 11.1.x which it wants to merge into, but I guess it's testing against 11.x?

acbramley’s picture

Status: Needs work » Needs review

I've rebased the MR onto 11.x, hopefully it went well 🤞

jigarius’s picture

Status: Needs review » Reviewed & tested by the community

Just tested it to be working fine with Drupal 10.0.9.

jigarius’s picture

Status: Reviewed & tested by the community » Needs work

After leaving comment #117, I ran into a new issue. There seems to be a permission anomaly for certain operations. I am marking this as needs work.

For example: With the changes proposed in the merge request at this point, I stop seeing the "Masquerade" operation for user entities even though I'm logged in as User 1. As soon as a I remove the patch, I start seeing it again. Additionally, I implemented a hook_entity_operation() in a custom module and the patch makes that custom operation disappear as well. Removing the patch brings it back.

twod’s picture

Yes, this will need some more rebase work once the issues mentioned in #115 are done.

The changes to the Renderer have been committed to the 11.x branch, but not the 10.x or 10.1.x branches (now committed to 10.1), so if you are using D10 you are likely missing the corresponding change from that issue - which is likely to cause problems like those you describe as cache metadata is lost in some situations.

acbramley’s picture

Status: Needs work » Needs review

It'd be good to get some steps to reproduce #118 or the sample code for the custom operation. We are running this patch with many custom operations and all work as intended.

acbramley’s picture

For example: With the changes proposed in the merge request at this point, I stop seeing the "Masquerade" operation for user entities even though I'm logged in as User 1. As soon as a I remove the patch, I start seeing it again.

I just debugged this and it's in fact correct. The masquerade operation's URL route is entity.user.masquerade

That route has the _csrf_token requirement. That gets removed by the array_diff in AccessManager::check because there is no request, which the csrf_token requirement needs.

thefancywizard’s picture

I created a 10.1.x-compatible patch from the merge request changes if anyone is in need.

acbramley’s picture

Hiding patches to avoid confusion.

smustgrave’s picture

Appears to have gone through a few rounds of reviews

All threads appear to have been addressed in the MR. Took a look at the MR and nothing stands out. But seems like a large change so will wait to see if anyone else chimes in.

thursday_bw’s picture

Status: Needs review » Reviewed & tested by the community

#117 set this to reviewed and tested by the community.
#118 re-set this to needs work with a vague bug report.
#121 spoke to the bug report and reported it as correct (meaning that 'works as expected' I believe).

Therefore setting this back to 'Reviewed and tested by the community'.

I have done some testing and can confirm that MR3556 as of 14 Jan 2024 resolves the entity operations problems reported on this issue, and also the username issues reported on Incorrect user linked on content page when switching users.

bircher’s picture

StatusFileSize
new26.38 KB

Attaching also a patch for Drupal 10.2.x

mxr576’s picture

Checked the latest state of MR, also RTBC++.

larowlan’s picture

Status: Reviewed & tested by the community » Needs work

Left some minor comments on the MR

#85 asks the same question about sub-classes in contrib/custom code - I think we need an CR for that.

Did we get to the bottom of #121?

manthan.chauhan’s picture

StatusFileSize
new25.12 KB

I have reroll patch no 126 as per drupal version 10.3.1.

donaldp changed the visibility of the branch 2473873-views-entity-operations to active.

donaldp changed the visibility of the branch 2473873-views-entity-operations to active.

gaëlg made their first commit to this issue’s fork.

gaëlg’s picture

StatusFileSize
new35.16 KB

I rebased the ...-10.1 branch against core 10.3.x, so that I could generate a patch against 10.3.5.
Not sure I did the right changes on core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php, I just blindly made the same number changes (if the commit added 42 to some number, I added 42 to that same number from core).

berdir’s picture

> That route has the _csrf_token requirement. That gets removed by the array_diff in AccessManager::check because there is no request, which the csrf_token requirement needs.

We tried the patch and did just run into this as well.

On one hand, I think that can be improved in masquerade and is arguably even a bug. The controller still checks access, so it's not a security issue.. masquerade_target_user_access() or masquerade_switch_user_validate() really should be exposed as an access check and the route should not _only_ have the csrf access check.

But I'm also not sure that the approach here is correct. It's kind a neat to fall back to the url access, but probably also has some performance overhead, because it involves creating route matches and lookups and directly doing access checks on the entity really should be faster. And it's an implicit API change anyway because all implementations need to now always return their URL.

An alternative, more "direct" approach would be to pass around a cacheability object to getDefaultOperations (tricky to add with BC) and the hook (easy to add an extra argument) and require that implementations add their access cacheability to do that and let calls deal with that. Similar to how hook_tokens() works for example.

lduong’s picture

StatusFileSize
new33.98 KB

I have rerolled patch #133 as per Drupal version 10.4.

acbramley changed the visibility of the branch 11.x to hidden.

acbramley changed the visibility of the branch 2473873-views-entity-operations-10.3.x to hidden.

acbramley changed the visibility of the branch 2473873-views-entity-operations-10.1.x to hidden.

acbramley changed the visibility of the branch 11.x to active.

acbramley changed the visibility of the branch 2473873-views-entity-operations-10.1.x to active.

acbramley changed the visibility of the branch 11.x to hidden.

acbramley changed the visibility of the branch 2473873-views-entity-operations to hidden.

acbramley changed the visibility of the branch 2473873-views-entity-operations-10.1.x to hidden.

acbramley’s picture

The previous MR was very out of date with a lot of merge conflicts and merge commits. I've squashed everything on to a new MR and hidden all the old branches. I think this is still NW but now we're up to date :D

quietone’s picture

jannakha’s picture

So my test case is:
I'm moving access checks from hook_ENTITY_TYPE_access into RouteSubscriber.

I've got a custom RouteSubscriber which adds Requirement with '_custom_access' based on user/entity on routes:
$collection->get('entity.node.delete_form')
$collection->get('entity.node.edit_form')

the routes are not accessible when conditions are met - as expected
but on views Edit/Delete operations are visible (although they lead to access denied/error page)

after applying patch #135 (to Drupal 10.4.5) - views operations are displayed as expected (no edit/delete ops when they should not be) just like when access was handled in hook_ENTITY_TYPE_access.
hurray!

what else is required to release this issue?

PS now the issue is "Delete" op appears on "Edit" form when it's forbidden by RouteSubscriber/custom access execution.
If I won't find an existing issue, I'll create a new issue for that. here's existing issue for Media https://www.drupal.org/project/drupal/issues/2998824

PPS back to hook_ENTITY_TYPE_access for now (unless there are better suggestions?)

acbramley’s picture

Status: Needs work » Needs review

Re #148 I think we need a review with fresh eyes. This was put back into NW in #118 with a bug in masquerade but @berdir pointed out this may be a bug in the module itself in #134

However, we may need to rethink this entirely based on that comment as well. I agree it's not the greatest solution, especially given the amount of seemingly unrelated changes made to get this to work.

thefancywizard’s picture

StatusFileSize
new26.97 KB

Attaching rerolled patch against 11.1.7

acbramley’s picture

Here's an alternative solution based on the end of #134 which is a bit easier now that we have the debug class loader (I think).

I copied the main test from https://git.drupalcode.org/project/drupal/-/merge_requests/11090 and it now passes with just the cacheability changes in https://git.drupalcode.org/project/drupal/-/merge_requests/12445

I think this is a lot more elegant, we can also add this to hook_entity_operation and hook_entity_operation_alter quite easily since hook BC won't be a concern afaik.

Keeping in NR to get a review of the overall approach, then I'll go ahead and do the hook changes as well.

acbramley changed the visibility of the branch 2473873-views-entity-operations-11.x to hidden.

acbramley’s picture

Issue summary: View changes
acbramley’s picture

Issue summary: View changes
acbramley’s picture

Title: Views entity operations lack cacheability support, resulting in incorrect dropbuttons » Entity operations lack cacheability support, resulting in incorrect dropbuttons

This is now fixing cacheability in both views and regular list builders.

godotislate’s picture

Status: Needs review » Needs work

Some minor nits and 1 question on the MR.

catch’s picture

ConfigEntityListBuilder and all its subclasses override this and make getDefaultOperations() public. Wild.

Do you know why? Might be worth a follow-up to try to remove the overrides, then we wouldn't need to update them in Drupal 12 except for when we delete them.

All the list builder classes will need to have this parameter added in D12, but not sure if all the list builder classes need to be updated now to indicate the change?

It would probably help anyone subclassing the subclasses if we did that, I don't think we've had a similar situation yet - the commented out added parameters is pretty new in itself.

acbramley’s picture

Status: Needs work » Needs review

Do you know why?

It looks like it might have been accidental. See the following commit where public function getOperations was changed to public function getDefaultOperations

https://git.drupalcode.org/project/drupal/-/commit/b504423ed07e9bb437e96...

Added #3538658: getDefaultOperations() in ConfigEntityListBuilder and sub classes should be protected

godotislate’s picture

Might be worth a follow-up to try to remove the overrides, then we wouldn't need to update them in Drupal 12 except for when we delete them.

There are a lot of them:

core/modules/block/src/BlockListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {
core/modules/block_content/src/BlockContentTypeListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {
core/modules/comment/src/CommentTypeListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {
core/modules/field_ui/src/FieldConfigListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {
core/modules/filter/src/FilterFormatListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {
core/modules/image/src/ImageStyleListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {
core/modules/menu_ui/src/MenuListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {
core/modules/node/src/NodeTypeListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {
core/modules/responsive_image/src/ResponsiveImageStyleListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {
core/modules/search/src/SearchPageListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {
core/modules/shortcut/src/ShortcutSetListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {
core/modules/taxonomy/src/VocabularyListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {
core/modules/user/src/RoleListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {
core/modules/views_ui/src/ViewListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {

Two content entity list builders too:

core/modules/menu_link_content/src/MenuLinkListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {
core/modules/menu_link_content/src/MenuLinkListBuilder.php
  public function getDefaultOperations(EntityInterface $entity) {

They do various things, so I don't think they can be removed.

Of relevance to this issue, these implementations of getDefaultOperations() include access checks inside them, so I wonder if cacheability needs to be captured.
core/modules/field_ui/src/FieldConfigListBuilder.php
core/modules/taxonomy/src/VocabularyListBuilder.php
core/modules/workspaces/src/WorkspaceListBuilder.php

godotislate’s picture

Status: Needs review » Needs work

Back to NW to look at whether cacheability needs to be handled in below bc of access checking per #160.

core/modules/field_ui/src/FieldConfigListBuilder.php
core/modules/taxonomy/src/VocabularyListBuilder.php
core/modules/workspaces/src/WorkspaceListBuilder.php

Also, to add the comment out parameter to all getDefaultOptions() implementations per #158.

acbramley’s picture

Status: Needs work » Needs review

Please let's not try to fix the cacheability of every list builder here, this is about granting the ability for operations to have cacheability in the first place. Those other list builders can be fixed in follow ups - the fixes may cause other issues and will balloon this MR. This is a 10 year old major bug that would be really good to get fixed.

I've added the commented out param to the subclasses.

godotislate’s picture

Status: Needs review » Reviewed & tested by the community

Those other list builders can be fixed in follow ups

Fair enough. Created #3540105: Capture access cacheability in getDefaultOperations() methods for all relevant entity list builders. This otherwise lgtm.

thefancywizard’s picture

StatusFileSize
new29.92 KB

Attached a patch version of the current state of MR !12445.
This provides a stable snapshot that can be used while the MR is still in progress, especially for projects that need to apply the fix immediately. No code changes were made — this patch reflects the MR as-is at the time of upload. This may be helpful for anyone wanting to apply the fix without tracking MR changes.

catch’s picture

Status: Reviewed & tested by the community » Fixed

This looks good, it's tricky with the bc for added parameters but at least we have a way to do it. Did my best with issue credit but this is a long issue with a lot of contributors so may have made mistakes.

Committed/pushed to 11.x, thanks!

  • catch committed 1ca1ad0f on 11.x
    Issue #2473873 by acbramley, simeonkesmev, berdir, xano, dawehner, twod...
catch’s picture

mxr576’s picture

Published the related CR

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

mxr576’s picture

Also opened a follow up for Drupal PHPStan: https://github.com/mglaman/phpstan-drupal/issues/930