diff --git a/js/checkbox-widget-3.js b/js/checkbox-widget-3.js index e0c2d3e76596e70a8134855a475b06da7f9e8373..a9ead16b55015067ca749c02f2d019569134dfbf 100644 --- a/js/checkbox-widget-3.js +++ b/js/checkbox-widget-3.js @@ -20,7 +20,7 @@ if ($checkboxWidgets.length > 0) { $checkboxWidgets.each(function (index, widget) { var $widget = $(widget); - var $widgetLinks = $widget.find('.facet-item > a', 'facet-item > span.drupal-masked-element'); + var $widgetLinks = $widget.find('.facet-item > a, facet-item > span.drupal-masked-element'); $widgetLinks.each(Drupal.facets.updateCheckbox); }); } @@ -37,7 +37,7 @@ if ($checkboxWidgets.length > 0) { $checkboxWidgets.each(function (index, widget) { var $widget = $(widget); - var $widgetLinks = $widget.find('.facet-item > a', 'facet-item > span.drupal-masked-element'); + var $widgetLinks = $widget.find('.facet-item > a, facet-item > span.drupal-masked-element'); // Add correct CSS selector for the widget. The Facets JS API will // register handlers on that element. diff --git a/js/checkbox-widget.js b/js/checkbox-widget.js index 5b3d36beed953adba004c2bb7321c96f679abc37..ccc0d44093ed1b9c1bd54fdb4e22567dfb003d12 100644 --- a/js/checkbox-widget.js +++ b/js/checkbox-widget.js @@ -20,7 +20,7 @@ if ($checkboxWidgets.length > 0) { $checkboxWidgets.each(function (index, widget) { var $widget = $(widget); - var $widgetLinks = $widget.find('.facet-item > a', 'facet-item > span.drupal-masked-element'); + var $widgetLinks = $widget.find('.facet-item > a, .facet-item > span.drupal-masked-element'); $widgetLinks.each(Drupal.facets.updateCheckbox); }); } @@ -37,7 +37,7 @@ if ($checkboxWidgets.length > 0) { $checkboxWidgets.each(function (index, widget) { var $widget = $(widget); - var $widgetLinks = $widget.find('.facet-item > a', 'facet-item > span.drupal-masked-element'); + var $widgetLinks = $widget.find('.facet-item > a, .facet-item > span.drupal-masked-element'); // Add correct CSS selector for the widget. The Facets JS API will // register handlers on that element. @@ -79,7 +79,19 @@ var $widget = $(this).closest('.js-facets-widget'); Drupal.facets.disableFacet($widget); - $widget.trigger('facets_filter', [ href ]); + if (!href) { + var checked = $(this).is(':checked'); + if (checked) { + $link.addClass('is-active'); + } + else { + $link.removeClass('is-active'); + } + $link.trigger('click'); + } + else { + $widget.trigger('facets_filter', [ href ]); + } }); if (active) { diff --git a/js/dropdown-widget.js b/js/dropdown-widget.js new file mode 100644 index 0000000000000000000000000000000000000000..e7f94cde9e0762223feb55721c6e805f4e36a65f --- /dev/null +++ b/js/dropdown-widget.js @@ -0,0 +1,125 @@ +/** + * @file + * Transforms links into a dropdown list. + */ + +(function ($, Drupal, once) { + + 'use strict'; + + Drupal.facets = Drupal.facets || {}; + Drupal.behaviors.facetsDropdownWidget = { + attach: function (context, settings) { + Drupal.facets.makeDropdown(context, settings); + } + }; + + /** + * Turns all facet links into a dropdown with options for every link. + * + * @param {object} context + * Context. + * @param {object} settings + * Settings. + */ + Drupal.facets.makeDropdown = function (context, settings) { + // Find all dropdown facet links and turn them into an option. + $(once('facets-dropdown-transform', '.js-facets-dropdown-links')).each(function () { + var $ul = $(this); + var $links = $ul.find('.facet-item a, .facet-item span.drupal-masked-element'); + var $dropdown = $('<select></select>'); + // Preserve all attributes of the list. + $ul.each(function () { + $.each(this.attributes,function (idx, elem) { + $dropdown.attr(elem.name, elem.value); + }); + }); + // Remove the class which we are using for .once(). + $dropdown.removeClass('js-facets-dropdown-links'); + + $dropdown.addClass('facets-dropdown'); + $dropdown.addClass('js-facets-widget'); + $dropdown.addClass('js-facets-dropdown'); + + var id = $(this).data('drupal-facet-id'); + // Add aria-labelledby attribute to reference label. + $dropdown.attr('aria-labelledby', "facet_" + id + "_label"); + var default_option_label = settings.facets.dropdown_widget[id]['facet-default-option-label']; + + // Add empty text option first. + var $default_option = $('<option></option>') + .attr('value', '') + .text(default_option_label); + $dropdown.append($default_option); + + $ul.prepend('<li class="default-option"><a href="' + window.location.href.split('?')[0] + '">' + Drupal.checkPlain(default_option_label) + '</a></li>'); + + var has_active = false; + $links.each(function () { + var $link = $(this); + var active = $link.hasClass('is-active'); + if ($link.attr('href')){ + var $option = $('<option></option>') + .attr('value', $link.attr('href')) + .data($link.data()); + } + else { + var $option = $('<option></option>') + .attr('value', $link.data('drupalFacetItemValue')) + .data($link.data()); + // take all data-x attributes from the link and add them to the option + + $.each($link[0].attributes, function () { + if (this.name.startsWith('data-')) { + $option.attr(this.name, this.value); + } + }); + $option.addClass('drupal-masked-element'); + } + if (active) { + has_active = true; + // Set empty text value to this link to unselect facet. + $default_option.attr('value', $link.attr('href')); + $ul.find('.default-option a, .default-option span').attr("href", $link.attr('href')); + $option.attr('selected', 'selected'); + $link.find('.js-facet-deactivate').remove(); + } + $option.text(function () { + // Add hierarchy indicator in case hierarchy is enabled. + var $parents = $link.parent('li.facet-item').parents('li.facet-item'); + var prefix = ''; + for (var i = 0; i < $parents.length; i++) { + prefix += '-'; + } + return prefix + ' ' + $link.text().trim(); + }); + $dropdown.append($option); + }); + + // Go to the selected option when it's clicked. + $dropdown.on('change.facets', function () { + var anchor = $($ul).find("[data-drupal-facet-item-id='" + $(this).find(':selected').data('drupalFacetItemId') + "']"); + var $linkElement = (anchor.length > 0) ? $(anchor) : $ul.find('.default-option a'); + + if ($linkElement.hasClass('drupal-masked-element')) { + $linkElement.trigger('click'); + } + else { + var url = $linkElement.attr('href'); + + $(this).trigger('facets_filter', [ url ]); + } + }); + + // Append empty text option. + if (!has_active) { + $default_option.attr('selected', 'selected'); + } + + // Replace links with dropdown. + $ul.after($dropdown).hide(); + Drupal.attachBehaviors($dropdown.parent()[0], Drupal.settings); + }); + }; + +})(jQuery, Drupal, once); diff --git a/link_obfuscation.api.php b/link_obfuscation.api.php new file mode 100644 index 0000000000000000000000000000000000000000..9df6a755c9484f94d4444cf2679258e94f9a41f2 --- /dev/null +++ b/link_obfuscation.api.php @@ -0,0 +1,22 @@ +<?php + +/** + * @file + * Api documentation for link_obfuscation module. + */ + +/** + * Alter results from $build['content']['#links'] for custom blocks. + * + * Here, we assign to the $items variable a reference to where the links for + * the language dropdown are stored. This is done so that we can modify the + * links directly. + */ +function hook_preprocess_block_items_alter($build, &$items) { + if (isset($build['content']['#links'])) { + $items = []; + foreach ($build['content']['#links'] as $key => &$link) { + $items[$key] = &$link; + } + } +} \ No newline at end of file diff --git a/link_obfuscation.links.menu.yml b/link_obfuscation.links.menu.yml new file mode 100644 index 0000000000000000000000000000000000000000..0f9b11c6093a7b574b6df5a3aa8871e138a8db46 --- /dev/null +++ b/link_obfuscation.links.menu.yml @@ -0,0 +1,5 @@ +link_obfuscation.settings: + title: 'Link obfuscation settings' + route_name: link_obfuscation.settings + parent: system.admin_config_search + weight: 140 \ No newline at end of file diff --git a/link_obfuscation.module b/link_obfuscation.module old mode 100644 new mode 100755 index a2232eb0f7e3df5479f96c24430ace8449ba9f49..e283eba096c6223ad747ba95e1df2a9d1ca262cc --- a/link_obfuscation.module +++ b/link_obfuscation.module @@ -5,6 +5,44 @@ * Contains link_obfuscation.module. */ +use Drupal\Core\Block\BlockPluginInterface; +use Drupal\link_obfuscation\BlockObfuscationHelper; + +/** + * Implements hook_block_view_alter(). + */ +function link_obfuscation_block_view_alter(array &$build, BlockPluginInterface $block) { + $build['#pre_render'][] = [ + BlockObfuscationHelper::class, + 'addObfuscationCacheContext', + ]; + + $build['#pre_render'][] = [ + BlockObfuscationHelper::class, + 'obfuscateMenuBlocks', + ]; + + $build['#pre_render'][] = [ + BlockObfuscationHelper::class, + 'obfuscateLanguageSwitcher', + ]; + + $build['#pre_render'][] = [ + BlockObfuscationHelper::class, + 'obfuscateBlockContent', + ]; +} + +/** + * Implements hook_views_post_render(). + */ +function link_obfuscation_views_post_render($view, array &$output, $cache) { + $output['#post_render'][] = [ + BlockObfuscationHelper::class, + 'obfuscateView', + ]; +} + /** * Implements hook_page_attachments_alter(). */ @@ -24,9 +62,10 @@ function link_obfuscation_library_info_alter(&$libraries, $extension) { $replacements = [ 'js/link-widget.js' => $newPath . '/link-widget.js', 'js/checkbox-widget.js' => $newPath . '/checkbox-widget.js', + 'js/dropdown-widget.js' => $newPath . '/dropdown-widget.js', ]; - foreach (['link-widget', 'checkbox-widget'] as $widget) { + foreach (['link-widget', 'checkbox-widget', 'dropdown-widget'] as $widget) { $libraryName = 'drupal.facets.' . $widget; if (isset($libraries[$libraryName])) { $new_js = []; @@ -43,3 +82,4 @@ function link_obfuscation_library_info_alter(&$libraries, $extension) { } } } + diff --git a/link_obfuscation.routing.yml b/link_obfuscation.routing.yml old mode 100644 new mode 100755 index ef21877a5eb2318fbff5f2055b2b919560da8b67..988b56577a8a03fb04b8cb400b831a1fd2eb6c82 --- a/link_obfuscation.routing.yml +++ b/link_obfuscation.routing.yml @@ -5,3 +5,11 @@ link_obfuscation.url: requirements: # Allow all access. _access: 'TRUE' + +link_obfuscation.settings: + path: '/admin/config/link-obfuscation/settings' + defaults: + _form: '\Drupal\link_obfuscation\Form\SettingsForm' + _title: 'Link obfuscation settings' + requirements: + _permission: 'administer site configuration' \ No newline at end of file diff --git a/src/BlockObfuscationHelper.php b/src/BlockObfuscationHelper.php new file mode 100644 index 0000000000000000000000000000000000000000..8ae25f7b9440b718159e0b3b96ffc587c15a9ccf --- /dev/null +++ b/src/BlockObfuscationHelper.php @@ -0,0 +1,273 @@ +<?php + +namespace Drupal\link_obfuscation; + +use Drupal\Core\Security\TrustedCallbackInterface; +use Drupal\Core\Url; + +/** + * Helper static class for block obfuscation. + */ +class BlockObfuscationHelper implements TrustedCallbackInterface { + + /** + * {@inheritdoc} + */ + public static function trustedCallbacks() { + return [ + 'obfuscateMenuBlocks', + 'addObfuscationCacheContext', + 'obfuscateLanguageSwitcher', + 'obfuscateBlockContent', + 'obfuscateView', + ]; + } + + /** + * {@inheritdoc} + */ + public static function obfuscateMenuBlocks(array $build): array { + $config = \Drupal::configFactory()->get('link_obfuscation.settings'); + + $blockIds = $config->get('menu_block_ids'); + if (empty($blockIds)) { + return $build; + } + + $blockIds = array_filter(explode("\r\n", $blockIds)); + + if (empty($build['#id']) || !in_array($build['#id'], $blockIds)) { + return $build; + } + + if (empty($build['content']['#items'])) { + return $build; + } + + $items = &$build['content']['#items']; + static::markItemsForObfuscation($items); + + return $build; + } + + /** + * {@inheritdoc} + */ + public static function addObfuscationCacheContext(array $build): array { + if (empty($build['content']['#items'])) { + return $build; + } + + $items = &$build['content']['#items']; + + if (static::differentObfuscationStatus($items)) { + $build['#cache']['contexts'][] = 'url.path.is_front'; + $build['#cache']['contexts'][] = 'languages:language_interface'; + } + + return $build; + } + + /** + * {@inheritdoc} + */ + public static function obfuscateLanguageSwitcher(array $build): array { + $config = \Drupal::configFactory()->get('link_obfuscation.settings'); + + $blockIds = $config->get('language_switcher_block_ids'); + if (empty($blockIds)) { + return $build; + } + + $blockIds = explode("\r\n", $blockIds); + if (empty($build['#id']) || !in_array($build['#id'], $blockIds)) { + return $build; + } + + /** @var \Drupal\Core\Path\PathMatcher $pathMatcher */ + $pathMatcher = \Drupal::service('path.matcher'); + $isFrontPage = $pathMatcher->isFrontPage(); + + /** @var \Drupal\Core\Language\LanguageManager $languageManager */ + $languageManager = \Drupal::languageManager(); + $isDefaultLanguage = $languageManager->getCurrentLanguage()->isDefault(); + + $obfuscate = FALSE; + + $obfuscateFrontDefaultLanguage = $config->get('obfuscate_language_switcher_on_front_page_default_language'); + $obfuscateFrontTranslationsLanguage = $config->get('obfuscate_language_switcher_on_front_page_not_default_language'); + $obfuscateAllPages = $config->get('obfuscate_language_switcher_on_all_pages'); + + if ($obfuscateFrontDefaultLanguage && $isFrontPage && $isDefaultLanguage) { + $obfuscate = TRUE; + } + + if ($obfuscateFrontTranslationsLanguage && $isFrontPage && !$isDefaultLanguage) { + $obfuscate = TRUE; + } + + if ($obfuscateAllPages && !$isFrontPage) { + $obfuscate = TRUE; + } + + $build['#cache']['contexts'][] = 'url.path.is_front'; + $build['#cache']['contexts'][] = 'languages:language_interface'; + + if ($obfuscate) { + $items = &$build['content']['#links']; + \Drupal::moduleHandler()->invokeAll('preprocess_block_items_alter', [$build, &$items]); + static::markItemsForObfuscation($items); + } + + return $build; + } + + /** + * {@inheritdoc} + */ + public static function obfuscateBlockContent(array $build) { + $config = \Drupal::configFactory()->get('link_obfuscation.settings'); + + $blockIds = $config->get('obfuscate_block_content_body_block_ids'); + if (empty($blockIds)) { + return $build; + } + + $blockIds = explode("\r\n", $blockIds); + if (empty($build['#id']) || !in_array($build['#id'], $blockIds)) { + return $build; + } + + $build['#cache']['contexts'][] = 'url.path.is_front'; + + if (empty($build['content']['body'][0]['#text'])) { + return $build; + } + + $obfuscateFront = $config->get('obfuscate_block_content_body_front'); + $obfuscateAllPages = $config->get('obfuscate_block_content_body_all_pages'); + + if (empty($obfuscateFront) && empty($obfuscateAllPages)) { + return $build; + } + + /** @var \Drupal\Core\Path\PathMatcher $pathMatcher */ + $pathMatcher = \Drupal::service('path.matcher'); + $isFrontPage = $pathMatcher->isFrontPage(); + + if ($obfuscateFront && !$isFrontPage || $obfuscateAllPages && $isFrontPage) { + return $build; + } + + /** @var \Drupal\link_obfuscation\Service\ObfuscateLinkService $obfuscateService */ + $obfuscateService = \Drupal::service('link_obfuscation.service'); + $obfuscateContent = $obfuscateService->obfuscateHtml($build['content']['body'][0]['#text']); + + $build['content']['body'][0]['#text'] = $obfuscateContent; + return $build; + } + + /** + * {@inheritdoc} + */ + public static function obfuscateView($output, array $element) { + $config = \Drupal::configFactory()->get('link_obfuscation.settings'); + $viewLines = $config->get('obfuscate_views_view_ids'); + if (empty($viewLines)) { + return $output; + } + $viewLines = explode("\r\n", $viewLines); + $viewLines = array_map(fn($blockId) => explode('|', $blockId), $viewLines); + + $viewIds = array_map(fn($viewLine) => $viewLine[0], $viewLines); + + if (empty($element['#view']->id()) || !in_array($element['#view']->id(), $viewIds)) { + return $output; + } + + $viewLines = array_filter($viewLines, fn($viewLine) => $viewLine[0] == $element['#view']->id() && $viewLine[1] == $element['#view']->current_display); + $viewLines = array_values($viewLines); + $obfuscateService = \Drupal::service('link_obfuscation.service'); + foreach ($viewLines as $viewLine) { + + $viewDisplayId = $viewLine[1]; + $linkContainer = $viewLine[2]; + + if (empty($viewDisplayId) || empty($linkContainer)) { + continue; + } + + if (empty($element['#view']->current_display) || $element['#view']->current_display != $viewDisplayId) { + continue; + } + $htmlContent = $obfuscateService->obfuscateHtml($output, $linkContainer); + + if (strcmp($htmlContent, $output) == 0) { + continue; + } + $output = $htmlContent; + } + + return $output; + } + + /** + * {@inheritdoc} + */ + protected static function differentObfuscationStatus(array $items): bool { + foreach ($items as $item) { + if (!is_array($item) || empty($item['url'])) { + continue; + } + + $url = $item['url']; + if (!$url instanceof Url) { + continue; + } + + $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'); + + $values = [ + $obfuscateOnEnFront, + $obfuscateOnTranslationFront, + $obfuscateOnEnAllPages, + $obfuscateOnTranslationAllPages, + ]; + + if (count(array_unique($values)) != 1) { + return TRUE; + } + + if (!empty($item['below'])) { + $differentStatusBelow = static::differentObfuscationStatus($item['below']); + if ($differentStatusBelow) { + return TRUE; + } + } + } + + return FALSE; + } + + /** + * {@inheritdoc} + */ + protected static function markItemsForObfuscation(array &$items) { + foreach ($items as &$item) { + /** @var \Drupal\Core\Url $url */ + $url = $item['url']; + $url->setOption('force_obfuscate', TRUE); + if (empty($url->getOption('query'))) { + $url->setOption('query', \Drupal::request()->query->all()); + } + if (!empty($item['below'])) { + static::markItemsForObfuscation($item['below']); + } + } + + } + +} diff --git a/src/Form/SettingsForm.php b/src/Form/SettingsForm.php new file mode 100644 index 0000000000000000000000000000000000000000..e492a51c7e511e808706d04645bb3e483314b7bd --- /dev/null +++ b/src/Form/SettingsForm.php @@ -0,0 +1,158 @@ +<?php + +namespace Drupal\link_obfuscation\Form; + +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Form\ConfigFormBase; +use Drupal\Core\Form\FormStateInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Obfuscation settings form. + */ +class SettingsForm extends ConfigFormBase { + + /** + * Drupal\Core\Entity\EntityTypeManagerInterface definition. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected $entityTypeManager; + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('entity_type.manager'), + ); + } + + /** + * {@inheritdoc} + */ + public function __construct(EntityTypeManagerInterface $entityTypeManager) { + $this->entityTypeManager = $entityTypeManager; + } + + /** + * {@inheritdoc} + */ + protected function getEditableConfigNames() { + return [ + 'link_obfuscation.settings', + ]; + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'link_obfuscation.settings'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $config = $this->config('link_obfuscation.settings'); + + $form['menu_block_ids'] = [ + '#type' => 'textarea', + '#rows' => 6, + '#title' => $this->t('Menu block ids to obfuscate'), + '#default_value' => $config->get('menu_block_ids'), + ]; + + $form['separator_0'] = [ + '#type' => 'html_tag', + '#tag' => 'hr', + ]; + + $form['obfuscate_language_switcher_on_front_page_default_language'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Obfuscate language switcher on front page on default language'), + '#default_value' => $config->get('obfuscate_language_switcher_on_front_default_page'), + ]; + + $form['obfuscate_language_switcher_on_front_page_not_default_language'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Obfuscate language switcher on front page on all translations but the default language'), + '#default_value' => $config->get('obfuscate_language_switcher_on_front_page_not_default_language'), + ]; + + $form['obfuscate_language_switcher_on_all_pages'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Obfuscate language switcher on pages'), + '#default_value' => $config->get('obfuscate_language_switcher_on_all_pages'), + ]; + + $form['language_switcher_block_ids'] = [ + '#type' => 'textarea', + '#rows' => 3, + '#title' => $this->t('Language switcher block id(s) to obfuscate'), + '#default_value' => $config->get('language_switcher_block_ids'), + ]; + + $form['separator_1'] = [ + '#type' => 'html_tag', + '#tag' => 'hr', + ]; + + $form['obfuscate_block_content_body_block_ids'] = [ + '#type' => 'textarea', + '#rows' => 3, + '#title' => $this->t('Block content ids to obfuscate'), + '#description' => $this->t('Only obfuscate links from the body field.'), + '#default_value' => $config->get('obfuscate_block_content_body_block_ids'), + ]; + + $form['obfuscate_block_content_body_front'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Obfuscate block content body on front page'), + '#default_value' => $config->get('obfuscate_block_content_body_front'), + ]; + + $form['obfuscate_block_content_body_all_pages'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Obfuscate block content body on all pages (except the front page)'), + '#default_value' => $config->get('obfuscate_block_content_body_all_pages'), + ]; + + $form['separator_2'] = [ + '#type' => 'html_tag', + '#tag' => 'hr', + ]; + + $form['obfuscate_views_view_ids'] = [ + '#type' => 'textarea', + '#rows' => 3, + '#title' => $this->t('View block ids to obfuscate'), + '#description' => $this->t('Obfuscate links from the view. The format should be view_id|display_id|container, for example + solr_search_views|global_search|pagination'), + '#default_value' => $config->get('obfuscate_views_view_ids'), + ]; + + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + parent::submitForm($form, $form_state); + + $this->config('link_obfuscation.settings') + ->set('menu_block_ids', $form_state->getValue('menu_block_ids')) + ->set('language_switcher_block_ids', $form_state->getValue('language_switcher_block_ids')) + ->set('obfuscate_language_switcher_on_front_page_default_language', $form_state->getValue('obfuscate_language_switcher_on_front_page_default_language')) + ->set('obfuscate_language_switcher_on_front_page_not_default_language', $form_state->getValue('obfuscate_language_switcher_on_front_page_not_default_language')) + ->set('obfuscate_language_switcher_on_all_pages', $form_state->getValue('obfuscate_language_switcher_on_all_pages')) + ->set('obfuscate_block_content_body_block_ids', $form_state->getValue('obfuscate_block_content_body_block_ids')) + ->set('obfuscate_block_content_body_front', $form_state->getValue('obfuscate_block_content_body_front')) + ->set('obfuscate_block_content_body_all_pages', $form_state->getValue('obfuscate_block_content_body_all_pages')) + ->set('obfuscate_views_view_ids', $form_state->getValue('obfuscate_views_view_ids')) + ->save(); + } + +} diff --git a/src/Plugin/facets/processor/ObfuscateLinkProcessor.php b/src/Plugin/facets/processor/ObfuscateLinkProcessor.php index a9e8a20d4b5d646a9f041aff0a2d0404b31336ff..97e1f10b7a2174e9f62f8272243765a82b4d4df5 100644 --- a/src/Plugin/facets/processor/ObfuscateLinkProcessor.php +++ b/src/Plugin/facets/processor/ObfuscateLinkProcessor.php @@ -94,21 +94,13 @@ class ObfuscateLinkProcessor extends ProcessorPluginBase implements BuildProcess * {@inheritdoc} */ public function supportsFacet(FacetInterface $facet) { - $data_definition = $facet->getDataDefinition(); - if ($data_definition->getDataType() === 'entity_reference') { - return TRUE; - } - if (!($data_definition instanceof ComplexDataDefinitionInterface)) { + $dataDefinition = $facet->getDataDefinition(); + // Here we can add unsupported data types when we discover them and decide we won't support them + $unsupportedDataTypes = []; + if (array_search($dataDefinition->getDataType(), $unsupportedDataTypes) !== FALSE) { return FALSE; } - - $property_definitions = $data_definition->getPropertyDefinitions(); - foreach ($property_definitions as $definition) { - if ($definition instanceof DataReferenceDefinitionInterface) { - return TRUE; - } - } - return FALSE; + return TRUE; } -} +} \ No newline at end of file diff --git a/src/Service/ObfuscateLinkService.php b/src/Service/ObfuscateLinkService.php old mode 100644 new mode 100755 index 3f3b2033d131f3a1786f06f185be7cf77ec68ba1..696ee26c157de89ad443754c65cba0d4d49c0f58 --- a/src/Service/ObfuscateLinkService.php +++ b/src/Service/ObfuscateLinkService.php @@ -2,8 +2,10 @@ namespace Drupal\link_obfuscation\Service; +use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Url; use Drupal\language\Entity\ConfigurableLanguage; +use Drupal\node\NodeInterface; use Masterminds\HTML5; /** @@ -77,65 +79,69 @@ class ObfuscateLinkService { /** * Obfuscate html. */ - public function obfuscateHtml(string $htmlText) { + public function obfuscateHtml(string $htmlText, ?string $class = NULL) { $html5 = new HTML5(); - - $htmlText = str_replace('<a', '<span', $htmlText); - $htmlText = str_replace('</a>', '</span>', $htmlText); - + libxml_use_internal_errors(TRUE); $domDocument = $html5->loadHTML($htmlText); - - foreach ($domDocument->getElementsByTagName('span') as $domLink) { - $href = $domLink->getAttribute('href'); - if (empty($href)) { - continue; + libxml_clear_errors(); + + $xpath = new \DOMXPath($domDocument); + $xpath->registerNamespace('x', 'http://www.w3.org/1999/xhtml'); + $changed = FALSE; + $query = !empty($class) + ? '//x:*[contains(concat(" ", normalize-space(@class), " "), " ' . $class . ' ")]//x:a' + : '//x:a'; + foreach ($xpath->query($query) as $domLink) { + $changed = TRUE; + $span = $domDocument->createElement('span'); + foreach ($domLink->attributes as $attribute) { + $span->setAttribute($attribute->name, $attribute->value); } - $url = NULL; - try { - $url = Url::fromUserInput($href); - } - catch (\Exception $e) { - // Ignore. + while ($domLink->childNodes->length > 0) { + $span->appendChild($domLink->childNodes->item(0)); } - if (empty($url)) { + $domLink->parentNode->replaceChild($span, $domLink); + + $href = $span->getAttribute('href'); + if (!empty($href)) { + $url = NULL; try { - $url = Url::fromUri($href); + $url = Url::fromUserInput($href); } catch (\Exception $e) { - // Ignore. + try { + $url = Url::fromUri($href); + } + catch (\Exception $e) { + // Ignore. + } } - } - if (empty($url)) { - continue; - } + if (!empty($url)) { + $attributes = static::getObfuscateAttributesForUrl($url); + foreach ($attributes as $name => $value) { + $span->setAttribute($name, $value); + } + } - $obfuscateAttributes = static::getObfuscateAttributesForUrl($url); - foreach ($obfuscateAttributes as $obfuscateAttributeName => $obfuscateAttribute) { - $domLink->setAttribute($obfuscateAttributeName, $obfuscateAttribute); - } + if ($span->hasAttribute('target')) { + $span->setAttribute('data-target', $span->getAttribute('target')); + $span->removeAttribute('target'); + } - if ($domLink->getAttribute('target')) { - $domLink->setAttribute('data-target', $domLink->getAttribute('target')); - $domLink->removeAttribute('target'); + $span->removeAttribute('rel'); + $span->removeAttribute('hreflang'); + $classes = trim($span->getAttribute('class') . ' drupal-masked-element'); + $span->setAttribute('class', $classes); + $span->removeAttribute('href'); } - - $domLink->removeAttribute('rel'); - $domLink->removeAttribute('hreflang'); - - $classes = $domLink->getAttribute('class'); - $classes .= ' drupal-masked-element'; - $classes = trim($classes); - - $domLink->setAttribute('class', $classes); - $domLink->removeAttribute('href'); } - - $obfuscatedContent = $domDocument->saveHTML(); - - return $obfuscatedContent; + if (!$changed) { + return $htmlText; + } + return $domDocument->saveHTML(); } }