refreshless-8.x-1.x-dev/modules/refreshless_turbo/src/Hooks/Prefetch.php
modules/refreshless_turbo/src/Hooks/Prefetch.php
<?php
declare(strict_types=1);
namespace Drupal\refreshless_turbo\Hooks;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Asset\AttachedAssetsInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Url;
use Drupal\hux\Attribute\Alter;
use Drupal\hux\Attribute\Hook;
use function array_unique;
/**
* Prefetch hook implementations.
*/
class Prefetch {
/**
* Hook constructor; saves dependencies.
*
* @param \Drupal\Core\Session\AccountProxyInterface $currentUser
* The current user account proxy service.
*/
public function __construct(
protected readonly AccountProxyInterface $currentUser,
) {}
/**
* Array of route names in link elements to disable Turbo prefetching on.
*
* @var string[]
*/
protected array $excludedRoutesLink = [
'devel.cache_clear',
'devel.run_cron',
'entity.block.disable',
'entity.block.enable',
'entity.shortcut.link_delete_inline',
'shortcut.link_add_inline',
'system.admin_compact_page',
'system.batch_page.html',
'system.cron',
'system.run_cron',
'system.theme_install',
'system.theme_set_default',
'system.theme_uninstall',
'update.manual_status',
'user.logout',
'user.logout.http',
];
/**
* Array of route names to disable Turbo prefetching on via drupalSettings.
*
* @var string[]
*/
protected array $excludedRoutesDrupalSettings = [
'user.logout',
];
#[Alter('link')]
/**
* Implements hook_link_alter().
*
* This adds the 'data-turbo-prefetch="false"' attribute to various
* administration links that would perform unexpected actions when prefetched
* by Turbo.
*
* @see \Drupal\Core\Url
*
* @see https://www.drupal.org/project/refreshless/issues/3423544
* Issue detailing the problem.
*/
public function alterLink(array &$variables): void {
if (
$variables['url']->isExternal() ||
!$variables['url']->isRouted() ||
!\in_array(
$variables['url']->getRouteName(), $this->excludedRoutesLink,
)
) {
return;
}
$variables['options']['attributes']['data-turbo-prefetch'] = 'false';
// Also prevent preloading on these routes as that causes the same problems
// as prefetching.
$variables['options']['attributes'][
'data-refreshless-lazy-preload'
] = 'false';
}
#[Hook('js_settings_build')]
/**
* Output pretch exclusion path to drupalSettings.
*
* @todo Should we cache this or does Drupal handle that? It's unclear if
* this will scale well so it may need to be looked at down the line if it
* becomes a performance issue.
*/
public function outputExcludePaths(
array &$settings, AttachedAssetsInterface $assets,
): void {
$paths = [];
foreach ($this->excludedRoutesDrupalSettings as $routeName) {
try {
$url = Url::fromRoute($routeName);
} catch (\Exception $exception) {
continue;
}
// Don't output paths the user doesn't have access to so that we don't
// disclose potentially sensitive information.
if ($url->access($this->currentUser) === false) {
continue;
}
$generatedPath = $url->toString();
// We need to parse this to output only the path itself without any query
// parameters that may be present such as CSRF tokens, etc., as they will
// prevent matching.
$parsed = UrlHelper::parse($generatedPath);
$paths[] = $parsed['path'];
}
$settings['refreshless']['prefetchExcludePaths'] = array_unique($paths);
}
}
