0

I working on the advanced_pwa module. My use case is that I want to tell the client side service worker to flush it's cache when a user logs out.

I concluded that creating a event subscriber would be the way to go. I defined a library that has a JS file to send the service worker the message. I'm trying to have that script run on the logout event.

The event isn't firing on the basis that I'm using the object_log module to log the event object for testing purposes.

Here's my implementation. I'm seeing no errors anywhere.

// advanced_pwa.services.yml

services:
  advanced_pwa.push_notifications:
    class: Drupal\advanced_pwa\Model\SubscriptionsDatastorage
  advanced_pwa.logout_subscriber:
    class: Drupal\advanced_pwa\EventSubscriber\AdvancedPwaLogoutSubscriber
    arguments: ['@library.discovery']
    tags:
      - { name: event_subscriber }

// src/EventSubscriber/AdvancedPwaLogoutSubscriber.php

<?php

namespace Drupal\advanced_pwa\EventSubscriber;

use Drupal\Core\Asset\LibraryDiscoveryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\Event;

class AdvancedPwaLogoutSubscriber implements EventSubscriberInterface {

  protected $libraryDiscovery;

  public function __construct(LibraryDiscoveryInterface $library_discovery) {
    $this->libraryDiscovery = $library_discovery;
  }

  public static function getSubscribedEvents() {
    $events = [];
    $events['user.logout'] = 'onPwaUserLogout';
    return $events;
  }

  public function onPwaUserLogout(Event $event) {

    // Check if code is executed
    object_log('logout event fired', $event);
    
    // Attach the JavaScript library to clear the service worker cache.
    $libraries = $this->libraryDiscovery->getLibrariesByName('advanced_pwa/logout');
    $event->getResponse()->addLibrary(reset($libraries));
  }

}

I don't think this is relevant but here is the library definition

advanced_pwa.logout:
  version: 1.x
  js:
    js/logout.js: {}
  dependencies:
    - core/jquery
    - core/drupal

I'm testing this on Drupal 10. The answer must be staring me in the face. I've been on this for 2 days now.

2 Answers 2

2

There is no logout event; for now, you can either use the MR (kind of difficult for a contrib module) or hook_user_logout().

You might also look at the Hook Event Dispatcher contrib module.

4
  • I've tried both of those ideas. Neither has got me there. I can confirm that when I flush the drupal cache, the getSubscribedEvents() is read. I added an object_log() in there.
    – gMaximus
    Commented May 16 at 15:57
  • function advanced_pwa_user_logout(\Drupal\Core\Session\AccountInterface $account) { // Get the event dispatcher service. $event_dispatcher = \Drupal::service('event_dispatcher'); $event_dispatcher->dispatch(new GenericEvent('user.logout', ['account' => $account])); }
    – gMaximus
    Commented May 16 at 16:01
  • Can I have my JS run from within the hook_user_logout without using the events system?
    – gMaximus
    Commented May 16 at 20:17
  • 2
    You can use $event->getResponse() only in a KernelEvents::RESPONSE, so you would need a service class subscribing to both events. The custom one you dispatch and the one dispatched by the kernel. See drupal.stackexchange.com/a/291504/47547
    – 4uk4
    Commented May 16 at 22:31
0

So days later with the help of others here. This is what I'm going with:

// src/EventSubscriber/AdvancedPwaLogoutSubscriber.php

<?php

namespace Drupal\advanced_pwa\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpFoundation\RedirectResponse;

/**
 * Event subscriber to run JavaScript when a user logs out.
 */
class AdvancedPwaLogoutSubscriber implements EventSubscriberInterface {

  /**
   * Adds JavaScript to the response on user logout.
   *
   * @param \Symfony\Component\HttpKernel\Event\ResponseEvent $event
   *   The kernel event.
   */
  public function onLogoutResponse(ResponseEvent $event) {
    $route_name = \Drupal::routeMatch()->getRouteName();
    if ($route_name == 'user.logout') {
      $response = $event->getResponse();

      if ($response instanceof RedirectResponse) {
        // Add a query parameter to signal the logout action
        $url = $response->getTargetUrl();
        $url .= (parse_url($url, PHP_URL_QUERY) ? '&' : '?') . 'pwa_logout=1';
        $response->setTargetUrl($url);
      }
    }

    // Check if the request has the logout query parameter and add the script
    if ($event->getRequest()->query->get('pwa_logout')) {
      $response = $event->getResponse();
      if ($response->headers->get('Content-Type') === 'text/html; charset=UTF-8') {
        $content = $response->getContent();
        $script = "
        <script>
        CUSTOM SCRIPT to EXECUTE
        </script>
        ";
        $content = str_replace('</body>', $script . '</body>', $content);
        $response->setContent($content);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    return [
      KernelEvents::RESPONSE => 'onLogoutResponse',
    ];
  }

}

// advanced_pwa.services.yml

services:
  advanced_pwa.logout_subscriber:
    class: Drupal\advanced_pwa\EventSubscriber\AdvancedPwaLogoutSubscriber
    tags:
      - { name: event_subscriber  }

This will send my service worker the logout message when the user.logout route is called.

I went this way (having JS in PHP), instead of the original idea of implementing a library with this JS in it because I'd spent days going around in circles. This way made my problem go away, rightly or wrongly.

Although my use case was executing JS during the logout process, the technique could be used to make other changes on other Drupal routes.

Everything within this if statement if ($route_name == 'user.logout') { ... } is unique for all cases. Unless you're also wanting to execute some JS. I hope it helps others.

Remember to switch out references to advanced_pwa with your own module name.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.