Problem/Motivation

We currently (ab)use the #post_render_cache mechanism to apply response-wide replacements.

But, in #2469431: BigPipe for auth users: first send+render the cheap parts of the page, then the expensive parts, we are trying to get rid of #post_render_cache, and the setting of the "is-active" class is the only problematic use of it. Quoting myself in #2469431-40: BigPipe for auth users: first send+render the cheap parts of the page, then the expensive parts:

  1. I think we should consider removing #post_render_cache. The #attached case for #post_render_cache only has a single use case in core (history_attach_timestamp()), which could also be handled by a placeholder that returns the empty string as #markup, but does set #attached.
    The only case in core for which this is wrong, is setting the "active" class on links for anonymous users, which is done by the SystemController::\Drupal\system\Controller\SystemController::setLinkActiveClass() #post_render_cache callback. But, that's such a very very very special case, and actually, Symfony already has a nice abstraction for it: the FilterResponseEvent. (IMO we should use that mechanism anyway, regardless of this issue.)
    That'd allow us to keep things simple, and in fact, make things simpler compared to HEAD.

Proposed resolution

Symfony already has a nice abstraction for this: FilterResponseEvent/KernelEvents::RESPONSE. Use that instead.

Remaining tasks

TBD

User interface changes

None.

API changes

None.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Wim Leers’s picture

Status: Active » Needs review
FileSize
6.17 KB

Here's the patch with minimal changes.

Wim Leers’s picture

FileSize
18.68 KB
14.51 KB

Now all logic and tests migrated, but still minimal changes in the tests.

Status: Needs review » Needs work

The last submitted patch, 2: anon_active_links-2478443-2.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
5.15 KB
22.7 KB

Partially updated the unit tests; now no mention of render arrays anymore.

(Also rebased, which makes the patch applicable again.)

dawehner’s picture

  1. +++ b/core/core.services.yml
    @@ -1351,3 +1351,9 @@ services:
    +  active_link_response_filter:
    

    should we namespace them, like response_filter.active_link?

  2. +++ b/core/lib/Drupal/Core/EventSubscriber/ActiveLinkResponseFilter.php
    @@ -0,0 +1,234 @@
    +  /**
    +   * @param AccountInterface $current_user
    +   * @param RouteMatchInterface $route_match
    +   * @param PathMatcherInterface $path_matcher
    +   * @param LanguageManagerInterface $language_manager
    +   */
    

    docs :)

  3. +++ b/core/lib/Drupal/Core/EventSubscriber/ActiveLinkResponseFilter.php
    @@ -0,0 +1,234 @@
    +    // Only care about HTML responses.
    +    if ($event->getRequest()->getRequestFormat() !== 'html') {
    +      return;
    +    }
    

    are we sure about that? Don't we want to do the active link handling also on modal/dialog requests for example?

  4. +++ b/core/lib/Drupal/Core/EventSubscriber/ActiveLinkResponseFilter.php
    @@ -0,0 +1,234 @@
    +    $current_path = $this->routeMatch->getRouteName() ? Url::fromRouteMatch($this->routeMatch)->getInternalPath() : '';
    

    Should we use the current_path service instead?

  5. +++ b/core/lib/Drupal/Core/EventSubscriber/ActiveLinkResponseFilter.php
    @@ -0,0 +1,234 @@
    +    $events[KernelEvents::RESPONSE][] = ['onResponse', -512];
    

    Any reason to choose that late handling?

Status: Needs review » Needs work

The last submitted patch, 1: anon_active_links-2478443-1.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
23.42 KB
4.39 KB

… and the remaining changes. I think this is sufficiently clean.

Wim Leers’s picture

FileSize
23.59 KB
3.69 KB
  1. Sure, done :)
  2. Fixed.
  3. Yes, I'm sure, because in HEAD this is being set in hook_page_attachments(), which definitely only runs for HTML responses, because it is invoked by HtmlRenderer.
    AFAIK we've never done "active link" handling inside modals/dialogs. Doing that can be a normal follow-up.
  4. Oooh! Nice catch :) Done.
  5. Only one reason: because logically speaking, this should run *very* late. It should run after anything else that modifies the markup. That's all. Happy to change it!
dawehner’s picture

Only one reason: because logically speaking, this should run *very* late. It should run after anything else that modifies the markup. That's all. Happy to change it!

Well, just document the intent, I think its fine to do so.

Wim Leers’s picture

Issue summary: View changes

The last submitted patch, 4: anon_active_links-2478443-3.patch, failed testing.

Wim Leers’s picture

FileSize
23.59 KB
772 bytes

#9: done.

The last submitted patch, 7: anon_active_links-2478443-5.patch, failed testing.

Wim Leers’s picture

FileSize
24.53 KB
1.66 KB

Fixed the failures in #1.

EDIT: AFAICT that fixed all failures!

The last submitted patch, 8: anon_active_links-2478443-8.patch, failed testing.

Wim Leers’s picture

The last submitted patch, 12: anon_active_links-2478443-11.patch, failed testing.

Fabianx’s picture

+++ b/core/modules/system/system.module
@@ -596,25 +597,15 @@ function system_page_attachments(array &$page) {
   // @see \Drupal\system\Controller\SystemController::setLinkActiveClass
+  // @see \Drupal\Core\EventSubscriber\ActiveLinkResponseFilter

nit - wrong @see for SystemController

--

Besides that, as this only concerns the move from system_page_attachmetns to the finish response subscriber, this is good to go.

RTBC once this nit is fixed.

Train of thought

I still think things like CSRF tokens, etc. or the JS to add the active-class library belong on the link render array and not system wide.

The consequence of doing that system wide is that whenever we use placeholdering we also need to add that library as the FinishResponseSubscriber (and the previous #prc) won't cut it for fragments / placeholders. That however is a solvable problem and should be done in a follow-up.

Wim Leers’s picture

RE: train of thought: active-link.js asset library should be bubbled automatically when rendering links: agreed with that principle.

dawehner’s picture

It would be great if we could document in the issue summary that this is code just done for anonymous users, and by that will not require to run for bigpipe.

+++ b/core/lib/Drupal/Core/EventSubscriber/ActiveLinkResponseFilter.php
@@ -0,0 +1,241 @@
+    if (FALSE === stripos($event->getResponse()->headers->get('Content-Type'), 'text/html')) {

I thought we don't do yoda.

Wim Leers’s picture

Assigned: Wim Leers » Unassigned
FileSize
24.42 KB
1.02 KB

Fixed the nit!

Wim Leers’s picture

FileSize
24.56 KB
1.17 KB

#20:

  • Doc'd.
  • C/P'ed that style from Symfony subscribers. Fixed.
Fabianx’s picture

Status: Needs review » Reviewed & tested by the community

Yes, do yoda we don't. :-D

RTBC now, page_attachments_alter runs so late that its almost as late already as a FilterResponseSubscriber.

Crell’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/lib/Drupal/Core/EventSubscriber/ActiveLinkResponseFilter.php
    @@ -0,0 +1,246 @@
    +  public static function setLinkActiveClass($html_markup, $current_path, $is_front, $url_language, array $query) {
    

    There's no reason for this to be static. It's not getting used elsewhere (and should not be). If it really does need to be accessible elsewhere, then it should be its own service. Which given its complexity is not a bad idea anyway.

    (Yes I know it's a static in the current class that's being replaced, but campfire rules: Don't just move code around, improve it.)

  2. +++ b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
    @@ -165,6 +165,7 @@ public function renderResponse(array $main_content, Request $request, RouteMatch
         $response = new CacheableResponse($content, 200,[
    +      'Content-Type' => 'text/html; charset=UTF-8',
           'X-Generator' => 'Drupal ' . $version . ' (https://www.drupal.org)'
         ]);
    

    This will conflict with #2477461: Move X-Generator header to its own listener. Let's let that get in first as this would be easy to reroll.

Wim Leers’s picture

Status: Needs work » Reviewed & tested by the community
  1. 1) this patch is exactly about moving code around without improving it; it's the move itself that is beneficial, not the changes to the code. Let's keep moving forward.
    2) http://drupal4hu.com/node/416.html
    3) that'll complicate testability
    Therefore please let's do that in a minor/normal follow-up. This is major for a reason.
  2. Huh!? This patch is 10 times larger than the other. Both make trivial changes in this area, so both are easy to reroll. This is a major, that's a normal.

  • catch committed 046965d on 8.0.x
    Issue #2478443 by Wim Leers: Set the 'is-active' class for anonymous...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Yes agreed on the static method, we can discuss in a follow-up if at all.

I had the same question as dawehner about AJAX/modals but 1. it doesn't work now 2. I'm pretty sure it also doesn't work in 7.x since you'd not get the same for current_path() (at least for AJAX).

Committed/pushed to 8.0.x, thanks!

Status: Fixed » Closed (fixed)

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

damiankloip’s picture

How will making the method non-static complicate testability? Also, using a static method to signify a method does not change the state of an object just doesn't really seem like a 'thing'.

I agree with Crell, there is just no need. Minor issue though now...

Fabianx’s picture

I agree that we can improve that, but that was really out-of-scope for this issue.

Can we open a follow-up for that to cleanup, please? Thanks!