link_obfuscation-1.0.0-beta3/src/Service/ObfuscateLinkGenerator.php
src/Service/ObfuscateLinkGenerator.php
<?php
namespace Drupal\link_obfuscation\Service;
use Drupal\Component\Render\MarkupInterface;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Html;
use Drupal\Core\GeneratedButton;
use Drupal\Core\GeneratedLink;
use Drupal\Core\GeneratedNoLink;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Url;
use Drupal\Core\Utility\LinkGenerator;
/**
* Provides a class which generates a link with route names and parameters.
*/
class ObfuscateLinkGenerator extends LinkGenerator {
/**
* {@inheritdoc}
*
* For anonymous users, the "active" class will be calculated on the server,
* because most sites serve each anonymous user the same cached page anyway.
* For authenticated users, the "active" class will be calculated on the
* client (through JavaScript), only data- attributes are added to links to
* prevent breaking the render cache. The JavaScript is added in
* system_page_attachments().
*
* @see system_page_attachments()
*/
public function generate($text, Url $url) {
// The link generator should not modify the original URL object, this
// ensures consistent rendering.
// @see https://www.drupal.org/node/2842399
$url = clone $url;
// Performance: avoid Url::toString() needing to retrieve the URL generator
// service from the container.
$url->setUrlGenerator($this->urlGenerator);
if (is_array($text)) {
$text = $this->renderer->render($text);
}
// Start building a structured representation
// of our link to be altered later.
$variables = [
'text' => $text,
'url' => $url,
'options' => $url->getOptions(),
];
// Merge in default options.
$variables['options'] += [
'attributes' => [],
'query' => [],
'language' => NULL,
'set_active_class' => FALSE,
'absolute' => FALSE,
];
// Add a hreflang attribute if we know the language of this link's url and
// hreflang has not already been set.
if (!empty($variables['options']['language']) && !isset($variables['options']['attributes']['hreflang'])) {
$variables['options']['attributes']['hreflang'] = $variables['options']['language']->getId();
}
// Ensure that query values are strings.
array_walk($variables['options']['query'], function (&$value) {
if ($value instanceof MarkupInterface) {
$value = (string) $value;
}
});
// Set the "active" class if the 'set_active_class' option is not empty.
if (!empty($variables['options']['set_active_class']) && !$url->isExternal()) {
// Add a "data-drupal-link-query" attribute to let the
// drupal.active-link library know the query in a standardized manner.
if (!empty($variables['options']['query'])) {
$query = $variables['options']['query'];
ksort($query);
$variables['options']['attributes']['data-drupal-link-query'] = Json::encode($query);
}
// Add a "data-drupal-link-system-path" attribute to let the
// drupal.active-link library know the path in a standardized manner.
if ($url->isRouted() && !isset($variables['options']['attributes']['data-drupal-link-system-path'])) {
// @todo System path is deprecated - use the route name and parameters.
$system_path = $url->getInternalPath();
// Special case for the front page.
if ($url->getRouteName() === '<front>') {
$system_path = '<front>';
}
if (!empty($system_path)) {
$variables['options']['attributes']['data-drupal-link-system-path'] = $system_path;
}
}
}
// Remove all HTML and PHP tags from a tooltip,
// calling expensive strip_tags()
// only when a quick strpos() gives suspicion tags are present.
if (isset($variables['options']['attributes']['title']) &&
strpos($variables['options']['attributes']['title'], '<') !== FALSE) {
$variables['options']['attributes']['title'] = strip_tags($variables['options']['attributes']['title']);
}
// Allow other modules to modify the structure of the link.
$this->moduleHandler->alter('link', $variables);
$url = $variables['url'];
// Move attributes out of options since generateFromRoute() doesn't need
// them. Make sure the "href" comes first for testing purposes.
$attributes = ['href' => ''] + $variables['options']['attributes'];
unset($variables['options']['attributes']);
$url->setOptions($variables['options']);
// External URLs can not have cacheable metadata.
if ($url->isExternal()) {
$generated_link = new GeneratedLink();
$attributes['href'] = $url->toString(FALSE);
return $this->doLinkGenerate($url, $generated_link, $attributes, $variables);
}
if ($url->isRouted() && $url->getRouteName() === '<nolink>') {
$generated_link = new GeneratedNoLink();
unset($attributes['href'], $attributes['hreflang']);
return $this->doGenerate($generated_link, $attributes, $variables);
}
if ($url->isRouted() && $url->getRouteName() === '<button>') {
$generated_link = new GeneratedButton();
$attributes['type'] = 'button';
unset($attributes['href'], $attributes['hreflang']);
return $this->doGenerate($generated_link, $attributes, $variables);
}
$generated_url = $url->toString(TRUE);
$generated_link = GeneratedLink::createFromObject($generated_url);
// The result of the URL generator is a plain-text URL to use as the href
// attribute, and it is escaped by \Drupal\Core\Template\Attribute.
$attributes['href'] = $generated_url->getGeneratedUrl();
return $this->doLinkGenerate($url, $generated_link, $attributes, $variables);
}
/**
* {@inheritdoc}
*/
protected function doLinkGenerate(Url $url, $generated_link, $attributes, $variables) {
$obfuscateOnEnFront = $url->getOption('obfuscate_on_front_page');
$obfuscateOnTranslationFront = $url->getOption('obfuscate_on_translation_front_page');
$obfuscateOnEnAllPages = $url->getOption('obfuscate_on_all_pages');
$obfuscateOnTranslationAllPages = $url->getOption('obfuscate_on_all_translation_pages');
$forceObfuscate = $url->getOption('force_obfuscate');
/** @var \Drupal\Core\Path\PathMatcher $pathMatcher */
$pathMatcher = \Drupal::service('path.matcher');
$isFrontPage = $pathMatcher->isFrontPage();
/** @var \Drupal\Core\Language\LanguageManager $languageManager */
$languageManager = \Drupal::service('language_manager');
$isDefaultLanguage = $languageManager->getCurrentLanguage()->isDefault();
switch (TRUE) {
case $forceObfuscate:
case $isFrontPage && $isDefaultLanguage && $obfuscateOnEnFront:
case $isFrontPage && !$isDefaultLanguage && $obfuscateOnTranslationFront:
case !$isFrontPage && $isDefaultLanguage && $obfuscateOnEnAllPages:
case !$isFrontPage && !$isDefaultLanguage && $obfuscateOnTranslationAllPages:
return $this->doGenerateObfuscate($url, $generated_link, $attributes, $variables);
}
return $this->doGenerate($generated_link, $attributes, $variables);
}
/**
* Generates the obfuscated link.
*
* @param \Drupal\Core\Url $url
* Url.
* @param \Drupal\Core\GeneratedLink $generated_link
* The generated link, along with its associated cacheability metadata.
* @param array $attributes
* The attributes of the generated link.
* @param array $variables
* The link text, url, and other options.
*
* @return \Drupal\Core\GeneratedLink
* The generated link, along with its associated cacheability metadata.
*/
protected function doGenerateObfuscate(Url $url, GeneratedLink $generated_link, array $attributes, array $variables) {
if (!($variables['text'] instanceof MarkupInterface)) {
$variables['text'] = Html::escape($variables['text']);
}
$request = \Drupal::request();
$attributes = new Attribute($attributes);
$storage = $attributes->storage();
if (!empty($storage['target'])) {
$attributes->setAttribute('data-target', $storage['target']);
$attributes->removeAttribute('target');
}
$attributes->removeAttribute('hreflang');
$attributes->removeAttribute('rel');
$obfuscateAttributes = ObfuscateLinkService::getObfuscateAttributesForUrl($url);
foreach ($obfuscateAttributes as $attributeName => $attributeValue) {
$attributes->setAttribute($attributeName, $attributeValue);
}
if (!$url->isExternal() && $url->isRouted()) {
$routeName = $url->getRouteName();
if ($routeName == '<none>') {
return $this->doGenerate($generated_link, $attributes, $variables);
}
}
$attributes->addClass('drupal-masked-element');
$attributes->removeAttribute('data-drupal-link-system-path');
$attributes->removeAttribute('href');
if (!empty($storage['href']) && (string) $storage['href'] == $request->getRequestUri()) {
$attributes->addClass('active-link');
}
// This is safe because Attribute does escaping and $variables['text'] is
// either rendered or escaped.
return $generated_link->setGeneratedLink('<' . 'span' . $attributes . '>' . $variables['text'] . '</' . 'span' . '>');
}
}
