OutboundPathProcessorInterface::processOutbound $options['absolute'] does not work

Created on 17 January 2024, 5 months ago
Updated 1 February 2024, 5 months ago

Problem/Motivation

I am trying to use the OutboundPathProcessorInterface so that when a user clicks this link: '/node/7849', he is redirected to this URL:
'https://churchages.net/en/sermon/wesley/laying-the-foundation-of-the-new...';

If I do NOT use $options['absolute'] = TRUE, I get the full URL of my site prepended to the link output:

https://bible.booksai.org/node/https%3A/churchages.net/en/sermon/wesley/...

If I DO use $options['absolute'] = TRUE, I get nothing at all. When I hoover over the link, nothing appears. When I click on it, it takes me to a blank page: about:blank#blocked

Steps to reproduce

Created the class file:

namespace Drupal\solrai\PathProcessor;

use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
use Symfony\Component\HttpFoundation\Request;

class MyOutboundPathProcessor implements OutboundPathProcessorInterface {

	public function processOutbound($path, &$options = [], Request $request = NULL, $bubbleable_metadata = NULL) {
		// Check if the path is in your table
		$result = $this->lookupPathInTable($path);

		// If a matching destination path is found, replace it and set external if needed
		if ($result) {
			$path = $result['path'];
			if ($result['absolute']) {
				$options['absolute'] = TRUE;
			}
		}

		return $path;
	}

	private function lookupPathInTable($path) {
		// Define the mappings of paths to their respective URLs
		$pathMappings = [
			'/node/7849' => 'https://churchages.net/en/sermon/wesley/laying-the-foundation-of-the-new-chapel/',
			'/node/7850' => 'https://churchages.net/en/sermon/wesley/death-of-rev-mr-john-fletcher/'
		];

		// Check if the path exists in the mapping
		if (array_key_exists($path, $pathMappings)) {
			return [
				'path' => $pathMappings[$path],
				'absolute' => TRUE
			];
		}

		// Return null if no matching path is found
		return null;
	}
}

In the class file, if the incoming $path matches a mapping in my lookupPathInTable($path) method, then I return the external URL with $options['absolute'] = TRUE. However, this results in NO link: about:blank#blocked

All of the documentation I have found so far appear to state that the above is the way to redirect to an external URL using OutboundPathProcessorInterface, but it's not working.

Proposed resolution

If this is a bug, has it been resolved in newer core versions? If not, how do I resolve?

πŸ’¬ Support request
Status

Fixed

Version

10.0 ✨

Component
PathΒ  β†’

Last updated 6 days ago

  • Maintained by
  • πŸ‡¬πŸ‡§United Kingdom @catch
Created by

πŸ‡ΊπŸ‡ΈUnited States SomebodySysop

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @SomebodySysop
  • πŸ‡ΊπŸ‡ΈUnited States SomebodySysop

    The closest related issue I could find is this: https://www.drupal.org/project/drupal/issues/2873293 πŸ› OutboundPathProcessorInterface does not correctly handle absolute urls anymore Closed: works as designed

    Which was never resolved.

  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    Path processors are for paths not urls

    What are you trying to achieve?

  • πŸ‡ΊπŸ‡ΈUnited States SomebodySysop

    What I wish to achieve:

    Let's say I have a node, /node/210.

    I have a table called "source urls". This table contains local paths I want re-directed to external sites. If a match is found, then I would like the user automatically sent to the external site.

    So, when someone clicks on a link for this "/node/210" anywhere on my site, they are automatically redirected to the appropriate external site url.

    The reason for this: I am building an AI response API that would allow users to embed and deploy a chatbot on their site for their content without them having to build and maintain this infrastructure themselves. So, ideally, /node/210 would consist of data from their site that we have processed for AI interaction, and rather than have the end-user access the data on our site, they are directed to the actual source.

  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    You could build that with a link field from core and the field_redirection β†’ module

    Add a link field to the node-type that contains the source URL.

    Configure manage display for the node-type and set the formatter for the link field to a redirect.

    That would be a zero-code approach.

    If you'd prefer a code approach, I think you want an event subscriber that listens to KernelEvents::RESPONSE

    In the event subscriber you'd want something like this, this assumes you have a field called field_link_url on the node type, adapt as appropriate.

    /**
       * Force the redirect to a link field.
       */
      public function redirectToLinkField(ResponseEvent $event) {
        $request = $event->getRequest();
        if ($request->get('_route') !== 'entity.node.canonical') {
          return;
        }
    
        $node = $request->get('node');
        $bundle = $node->bundle();
    
        if ($bundle == 'YOUR_NODE_TYPE') {
          if ($node->hasField('field_link_url') && !$node->field_link_url->isEmpty()) {
            $uri = $node->field_link_url->first()->getUrl()->setAbsolute(TRUE);
            $redirect = new TrustedRedirectResponse($uri->toString(), 301 /* pick what you want here */);
            $redirect->setMaxAge(2764800 /* here too */);
            $event->setResponse($redirect);
          }
        }
      }
    
  • Status changed to Fixed 5 months ago
  • πŸ‡ΊπŸ‡ΈUnited States SomebodySysop

    @larowlan

    Thank you VERY much for that guidance. Exactly what I needed.

    Anyone else need to do something similar, this is how I utilized the Event Subscriber:

    namespace Drupal\solrai\EventSubscriber;
    
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    use Symfony\Component\HttpKernel\KernelEvents;
    use Symfony\Component\HttpKernel\Event\ResponseEvent;
    use Drupal\Core\Routing\TrustedRedirectResponse;
    
    class SolraiEventSubscriber implements EventSubscriberInterface {
    
      public static function getSubscribedEvents() {
        $events[KernelEvents::RESPONSE][] = ['redirectToExternalUrl', 0];
        return $events;
      }
    
      public function redirectToExternalUrl(ResponseEvent $event) {
        $request = $event->getRequest();
        if ($request->get('_route') !== 'entity.node.canonical') {
          return;
        }
    
        $node = $request->get('node');
        $nodePath = '/node/' . $node->id();
    
        $externalUrl = $this->lookupPathInTable($nodePath);
    
        if ($externalUrl) {
          $redirect = new TrustedRedirectResponse($externalUrl, 301);
          $redirect->setMaxAge(2764800);
          $event->setResponse($redirect);
        }
      }
    
      private function lookupPathInTable($path) {
        $pathMappings = [
    		'/node/7849' => 'https://churchages.net/en/sermon/wesley/laying-the-foundation-of-the-new-chapel/',
    		'/node/7850' => 'https://churchages.net/en/sermon/wesley/death-of-rev-mr-john-fletcher/'
          // Add more mappings as needed
        ];
    
        return $pathMappings[$path] ?? null;
      }
    }
    
    
  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.69.0 2024