From abbbe88e76e20e3d24d760372e5e3d51fc63b4e7 Mon Sep 17 00:00:00 2001 From: Brian Canini <canini.16@osu.edu> Date: Thu, 23 Jan 2020 11:57:14 -0500 Subject: [PATCH] applying patch to linkit - Linkit for Link field: https://www.drupal.org/project/linkit/issues/2712951 --- composer.json | 3 + vendor/composer/installed.json | 3 + web/modules/linkit/PATCHES.txt | 7 + .../linkit/config/schema/linkit.schema.yml | 18 + web/modules/linkit/js/linkit.autocomplete.js | 53 ++- web/modules/linkit/linkit.module | 2 +- web/modules/linkit/src/Entity/Profile.php | 14 + .../Field/FieldFormatter/LinkitFormatter.php | 188 ++++++++++ .../Plugin/Field/FieldWidget/LinkitWidget.php | 178 ++++++++++ .../Plugin/Linkit/Matcher/EmailMatcher.php | 5 +- .../Plugin/Linkit/Matcher/EntityMatcher.php | 6 + .../Linkit/Matcher/FrontPageMatcher.php | 12 +- web/modules/linkit/src/ProfileInterface.php | 11 + .../linkit/src/Utility/LinkitHelper.php | 192 ++++++++++ .../FunctionalJavascript/LinkFieldTest.php | 334 ++++++++++++++++++ .../FunctionalJavascript/LinkitDialogTest.php | 3 - .../src/Kernel/LinkitAutocompleteTest.php | 3 +- .../src/Kernel/LinkitEditorLinkDialogTest.php | 2 +- 18 files changed, 1014 insertions(+), 20 deletions(-) create mode 100644 web/modules/linkit/PATCHES.txt create mode 100644 web/modules/linkit/src/Plugin/Field/FieldFormatter/LinkitFormatter.php create mode 100644 web/modules/linkit/src/Plugin/Field/FieldWidget/LinkitWidget.php create mode 100644 web/modules/linkit/src/Utility/LinkitHelper.php create mode 100644 web/modules/linkit/tests/src/FunctionalJavascript/LinkFieldTest.php diff --git a/composer.json b/composer.json index 9a0b9757b1..2f545b2350 100644 --- a/composer.json +++ b/composer.json @@ -298,6 +298,9 @@ }, "drupal/smtp": { "2781157": "https://www.drupal.org/files/issues/2018-11-07/2781157-n10.patch" + }, + "drupal/linkit": { + "2712951": "https://www.drupal.org/files/issues/2019-11-27/linkit_for_link_field-2712951-140.patch" } } }, diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 14fc650cb0..7508cad86e 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -5639,6 +5639,9 @@ "status": "not-covered", "message": "Beta releases are not covered by Drupal security advisories." } + }, + "patches_applied": { + "2712951": "https://www.drupal.org/files/issues/2019-11-27/linkit_for_link_field-2712951-140.patch" } }, "installation-source": "dist", diff --git a/web/modules/linkit/PATCHES.txt b/web/modules/linkit/PATCHES.txt new file mode 100644 index 0000000000..2c2692ba41 --- /dev/null +++ b/web/modules/linkit/PATCHES.txt @@ -0,0 +1,7 @@ +This file was automatically generated by Composer Patches (https://github.com/cweagans/composer-patches) +Patches applied to this directory: + +2712951 +Source: https://www.drupal.org/files/issues/2019-11-27/linkit_for_link_field-2712951-140.patch + + diff --git a/web/modules/linkit/config/schema/linkit.schema.yml b/web/modules/linkit/config/schema/linkit.schema.yml index c6e3f1d10c..b8d487cbd0 100644 --- a/web/modules/linkit/config/schema/linkit.schema.yml +++ b/web/modules/linkit/config/schema/linkit.schema.yml @@ -112,3 +112,21 @@ ckeditor.plugin.drupallink: linkit_profile: type: string label: 'Linkit profile' + +# Schema for the Linkit widget. +field.widget.settings.linkit: + type: field.widget.settings.link_default + label: 'Linkit widget settings' + mapping: + linkit_profile: + type: string + label: 'Linkit profile' + +# Schema for the Linkit formatter. +field.formatter.settings.linkit: + type: field.formatter.settings.link + label: 'Linkit format settings' + mapping: + linkit_profile: + type: string + label: 'Linkit profile' diff --git a/web/modules/linkit/js/linkit.autocomplete.js b/web/modules/linkit/js/linkit.autocomplete.js index 558278ddff..b2ccca311d 100644 --- a/web/modules/linkit/js/linkit.autocomplete.js +++ b/web/modules/linkit/js/linkit.autocomplete.js @@ -63,21 +63,36 @@ * False to prevent further handlers. */ function selectHandler(event, ui) { - var $form = $(event.target).closest('form'); + var $context = $(event.target).closest('form,fieldset,tr'); + if (!ui.item.path) { throw 'Missing path param.' + JSON.stringify(ui.item); } - $('input[name="href_dirty_check"]', $form).val(ui.item.path); + $('input[name="href_dirty_check"]', $context).val(ui.item.path); if (ui.item.entity_type_id || ui.item.entity_uuid || ui.item.substitution_id) { if (!ui.item.entity_type_id || !ui.item.entity_uuid || !ui.item.substitution_id) { throw 'Missing path param.' + JSON.stringify(ui.item); } - - $('input[name="attributes[data-entity-type]"]', $form).val(ui.item.entity_type_id); - $('input[name="attributes[data-entity-uuid]"]', $form).val(ui.item.entity_uuid); - $('input[name="attributes[data-entity-substitution]"]', $form).val(ui.item.substitution_id); + } + $('input[name="attributes[href]"], input[name$="[attributes][href]"]', $context).val(ui.item.path); + $('input[name="attributes[data-entity-type]"], input[name$="[attributes][data-entity-type]"]', $context).val(ui.item.entity_type_id); + $('input[name="attributes[data-entity-uuid]"], input[name$="[attributes][data-entity-uuid]"]', $context).val(ui.item.entity_uuid); + $('input[name="attributes[data-entity-substitution]"], input[name$="[attributes][data-entity-substitution]"]', $context).val(ui.item.substitution_id); + + if (ui.item.label) { + // Automatically set the link title. + var $linkTitle = $(event.target).closest('.form-item').siblings('.form-type-textfield').find('.linkit-widget-title'); + if ($linkTitle.length > 0) { + if (!$linkTitle.val() || $linkTitle.hasClass('link-widget-title--auto')) { + // Set value to the label. + $linkTitle.val(ui.item.label); + + // Flag title as being automatically set. + $linkTitle.addClass('link-widget-title--auto'); + } + } } event.target.value = ui.item.path; @@ -158,7 +173,7 @@ // Act on textfields with the "form-linkit-autocomplete" class. var $autocomplete = $(context).find('input.form-linkit-autocomplete').once('linkit-autocomplete'); if ($autocomplete.length) { - $.widget('custom.autocomplete', $.ui.autocomplete, { + $.widget('ui.autocomplete', $.ui.autocomplete, { _create: function () { this._super(); this.widget().menu('option', 'items', '> :not(.linkit-result-line--group)'); @@ -169,10 +184,30 @@ // Use jQuery UI Autocomplete on the textfield. $autocomplete.autocomplete(autocomplete.options); - $autocomplete.autocomplete('widget').addClass('linkit-ui-autocomplete'); $autocomplete.click(function () { - $autocomplete.autocomplete('search', $autocomplete.val()); + var $this = $(this); + $this.autocomplete('search', $this.val()); + }); + + // Process each item. + $autocomplete.each(function () { + var $uri = $(this); + $uri.autocomplete('widget').addClass('linkit-ui-autocomplete'); + + $uri.closest('.form-item').siblings('.form-type-textfield').find('.linkit-widget-title') + .each(function() { + // Set automatic title flag if title is the same as uri text. + var $title = $(this); + var uriValue = $uri.val(); + if (uriValue && uriValue === $title.val()) { + $title.addClass('link-widget-title--auto'); + } + }) + .change(function () { + // Remove automatic title flag. + $(this).removeClass('link-widget-title--auto'); + }); }); $autocomplete.on('compositionstart.autocomplete', function () { diff --git a/web/modules/linkit/linkit.module b/web/modules/linkit/linkit.module index affeaebf4d..14d14258b1 100644 --- a/web/modules/linkit/linkit.module +++ b/web/modules/linkit/linkit.module @@ -133,7 +133,6 @@ function linkit_form_editor_link_dialog_submit(array &$form, FormStateInterface } $fields = [ - 'href', 'data-entity-type', 'data-entity-uuid', 'data-entity-substitution', @@ -150,4 +149,5 @@ function linkit_form_editor_link_dialog_submit(array &$form, FormStateInterface } } } + } diff --git a/web/modules/linkit/src/Entity/Profile.php b/web/modules/linkit/src/Entity/Profile.php index 651242f830..070e98472a 100644 --- a/web/modules/linkit/src/Entity/Profile.php +++ b/web/modules/linkit/src/Entity/Profile.php @@ -6,6 +6,7 @@ use Drupal\Core\Entity\EntityWithPluginCollectionInterface; use Drupal\linkit\MatcherCollection; use Drupal\linkit\MatcherInterface; +use Drupal\linkit\Plugin\Linkit\Matcher\EntityMatcher; use Drupal\linkit\ProfileInterface; /** @@ -108,6 +109,19 @@ public function getMatcher($instance_id) { return $this->getMatchers()->get($instance_id); } + /** + * {@inheritdoc} + */ + public function getMatcherByEntityType($entity_type_id) { + foreach ($this->getMatchers() as $matcher) { + if ($matcher instanceof EntityMatcher && $matcher->getPluginDefinition()['target_entity'] === $entity_type_id) { + return $matcher; + } + } + + return NULL; + } + /** * {@inheritdoc} */ diff --git a/web/modules/linkit/src/Plugin/Field/FieldFormatter/LinkitFormatter.php b/web/modules/linkit/src/Plugin/Field/FieldFormatter/LinkitFormatter.php new file mode 100644 index 0000000000..775da51423 --- /dev/null +++ b/web/modules/linkit/src/Plugin/Field/FieldFormatter/LinkitFormatter.php @@ -0,0 +1,188 @@ +<?php + +namespace Drupal\linkit\Plugin\Field\FieldFormatter; + +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\Field\FieldItemListInterface; +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Path\PathValidatorInterface; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\link\LinkItemInterface; +use Drupal\link\Plugin\Field\FieldFormatter\LinkFormatter; +use Drupal\linkit\Entity\Profile; +use Drupal\linkit\SubstitutionManagerInterface; +use Drupal\linkit\Utility\LinkitHelper; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Plugin implementation of the 'linkit' formatter. + * + * @FieldFormatter( + * id = "linkit", + * label = @Translation("Linkit"), + * field_types = { + * "link" + * } + * ) + */ +class LinkitFormatter extends LinkFormatter implements ContainerFactoryPluginInterface { + + /** + * The substitution manager. + * + * @var \Drupal\linkit\SubstitutionManagerInterface + */ + protected $substitutionManager; + + /** + * The entity type manager. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected $entityTypeManager; + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $plugin_id, + $plugin_definition, + $configuration['field_definition'], + $configuration['settings'], + $configuration['label'], + $configuration['view_mode'], + $configuration['third_party_settings'], + $container->get('path.validator'), + $container->get('plugin.manager.linkit.substitution'), + $container->get('entity_type.manager') + ); + } + + /** + * Constructs a new LinkitFormatter. + * + * @param string $plugin_id + * The plugin_id for the formatter. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition + * The definition of the field to which the formatter is associated. + * @param array $settings + * The formatter settings. + * @param string $label + * The formatter label display setting. + * @param string $view_mode + * The view mode. + * @param array $third_party_settings + * Third party settings. + * @param \Drupal\Core\Path\PathValidatorInterface $path_validator + * The path validator service. + * @param \Drupal\linkit\SubstitutionManagerInterface $substitution_manager + * The substitution manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. + */ + public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, PathValidatorInterface $path_validator, SubstitutionManagerInterface $substitution_manager, EntityTypeManagerInterface $entity_type_manager) { + parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings, $path_validator); + $this->substitutionManager = $substitution_manager; + $this->entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function defaultSettings() { + return [ + 'linkit_profile' => 'default', + ] + parent::defaultSettings(); + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + $elements = parent::settingsForm($form, $form_state); + + $linkit_profiles = $this->entityTypeManager->getStorage('linkit_profile')->loadMultiple(); + + $options = []; + foreach ($linkit_profiles as $linkit_profile) { + $options[$linkit_profile->id()] = $linkit_profile->label(); + } + + $elements['linkit_profile'] = [ + '#type' => 'select', + '#title' => $this->t('Linkit profile'), + '#description' => $this->t('Must be the same as the profile selected on the form display for this field.'), + '#options' => $options, + '#default_value' => $this->getSetting('linkit_profile'), + ]; + + return $elements; + } + + /** + * {@inheritdoc} + */ + public function settingsSummary() { + $summary = parent::settingsSummary(); + + $linkit_profile_id = $this->getSetting('linkit_profile'); + $linkit_profile = $this->entityTypeManager->getStorage('linkit_profile')->load($linkit_profile_id); + + if ($linkit_profile) { + $summary[] = $this->t('Linkit profile: @linkit_profile', ['@linkit_profile' => $linkit_profile->label()]); + } + + return $summary; + } + + /** + * {@inheritdoc} + */ + public function viewElements(FieldItemListInterface $items, $langcode) { + $elements = parent::viewElements($items, $langcode); + + // Loop over the elements and substitute the URL. + foreach ($elements as $delta => &$item) { + /** @var \Drupal\link\LinkItemInterface $link_item */ + $link_item = $items->get($delta); + $substituted_url = $this->getSubstitutedUrl($link_item); + // Convert generated URL into a URL object. + if ($substituted_url && ($url = \Drupal::pathValidator()->getUrlIfValid($substituted_url->getGeneratedUrl()))) { + $item['#url'] = $url; + } + } + + return $elements; + } + + /** + * Returns a substitution URL for the given linked item. + * + * In case the items links to an entity use a substituted/generated URL. + * + * @param \Drupal\link\LinkItemInterface $item + * The link item. + * + * @return \Drupal\Core\GeneratedUrl|null + * The substitution URL, or NULL if not able to retrieve it from the item. + */ + protected function getSubstitutedUrl(LinkItemInterface $item) { + if (parse_url($item->uri, PHP_URL_SCHEME) == 'entity') { + if ($entity = LinkitHelper::getEntityFromUri($item->uri)) { + $profile = Profile::load($this->getSettings()['linkit_profile']); + + /** @var \\Drupal\linkit\Plugin\Linkit\Matcher\EntityMatcher $matcher */ + $matcher = $profile->getMatcherByEntityType($entity->getEntityTypeId()); + $substitution_type = $matcher ? $matcher->getConfiguration()['settings']['substitution_type'] : SubstitutionManagerInterface::DEFAULT_SUBSTITUTION; + return $this->substitutionManager->createInstance($substitution_type)->getUrl($entity); + } + } + + return NULL; + } + +} diff --git a/web/modules/linkit/src/Plugin/Field/FieldWidget/LinkitWidget.php b/web/modules/linkit/src/Plugin/Field/FieldWidget/LinkitWidget.php new file mode 100644 index 0000000000..d2f7823bfb --- /dev/null +++ b/web/modules/linkit/src/Plugin/Field/FieldWidget/LinkitWidget.php @@ -0,0 +1,178 @@ +<?php + +namespace Drupal\linkit\Plugin\Field\FieldWidget; + +use Drupal\Core\Url; +use Drupal\link\Plugin\Field\FieldWidget\LinkWidget; +use Drupal\Core\Field\FieldItemListInterface; +use Drupal\Core\Form\FormStateInterface; +use Drupal\linkit\Utility\LinkitHelper; + +/** + * Plugin implementation of the 'linkit' widget. + * + * @FieldWidget( + * id = "linkit", + * label = @Translation("Linkit"), + * field_types = { + * "link" + * } + * ) + */ +class LinkitWidget extends LinkWidget { + + /** + * {@inheritdoc} + */ + public static function defaultSettings() { + return [ + 'linkit_profile' => 'default', + ] + parent::defaultSettings(); + } + + /** + * {@inheritdoc} + */ + public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { + $item = $items[$delta]; + $uri = $item->uri; + $uri_scheme = parse_url($uri, PHP_URL_SCHEME); + if (!empty($uri) && empty($uri_scheme)) { + $uri = LinkitHelper::uriFromUserInput($uri); + $uri_scheme = parse_url($uri, PHP_URL_SCHEME); + } + $uri_as_url = !empty($uri) ? Url::fromUri($uri)->toString() : ''; + $linkit_profile_id = $this->getSetting('linkit_profile'); + + // The current field value could have been entered by a different user. + // However, if it is inaccessible to the current user, do not display it + // to them. + $default_allowed = !$item->isEmpty() && (\Drupal::currentUser()->hasPermission('link to any page') || $item->getUrl()->access()); + + if ($default_allowed && $uri_scheme == 'entity') { + $entity = LinkitHelper::getEntityFromUri($uri); + } + + $element['uri'] = [ + '#type' => 'linkit', + '#title' => $this->t('URL'), + '#placeholder' => $this->getSetting('placeholder_url'), + '#default_value' => $default_allowed ? $uri_as_url : NULL, + '#maxlength' => 2048, + '#required' => $element['#required'], + '#description' => $this->t('Start typing to find content or paste a URL and click on the suggestion below.'), + '#autocomplete_route_name' => 'linkit.autocomplete', + '#autocomplete_route_parameters' => [ + 'linkit_profile_id' => $linkit_profile_id, + ], + '#error_no_message' => TRUE, + ]; + + $element['attributes']['href'] = [ + '#type' => 'hidden', + '#default_value' => $default_allowed ? $uri : '', + ]; + + $element['attributes']['data-entity-type'] = [ + '#type' => 'hidden', + '#default_value' => $default_allowed && isset($entity) ? $entity->getEntityTypeId() : '', + ]; + + $element['attributes']['data-entity-uuid'] = [ + '#type' => 'hidden', + '#default_value' => $default_allowed && isset($entity) ? $entity->uuid() : '', + ]; + + $element['attributes']['data-entity-substitution'] = [ + '#type' => 'hidden', + '#default_value' => $default_allowed && isset($entity) ? $entity->getEntityTypeId() == 'file' ? 'file' : 'canonical' : '', + ]; + + $element['title'] = [ + '#type' => 'textfield', + '#title' => $this->t('Link text'), + '#placeholder' => $this->getSetting('placeholder_title'), + '#default_value' => isset($items[$delta]->title) ? $items[$delta]->title : NULL, + '#maxlength' => 255, + '#access' => $this->getFieldSetting('title') != DRUPAL_DISABLED, + '#attributes' => [ + 'class' => ['linkit-widget-title'], + ], + '#error_no_message' => TRUE, + ]; + // Post-process the title field to make it conditionally required if URL is + // non-empty. Omit the validation on the field edit form, since the field + // settings cannot be saved otherwise. + if (!$this->isDefaultValueWidget($form_state) && $this->getFieldSetting('title') == DRUPAL_REQUIRED) { + $element['#element_validate'][] = [get_called_class(), 'validateTitleElement']; + } + + // If cardinality is 1, ensure a proper label is output for the field. + if ($this->fieldDefinition->getFieldStorageDefinition()->getCardinality() == 1) { + // If the link title is disabled, use the field definition label as the + // title of the 'uri' element. + if ($this->getFieldSetting('title') == DRUPAL_DISABLED) { + $element['uri']['#title'] = $element['#title']; + } + // Otherwise wrap everything in a details element. + else { + $element += [ + '#type' => 'fieldset', + ]; + } + } + + return $element; + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + $elements = parent::settingsForm($form, $form_state); + + $linkit_profiles = \Drupal::entityTypeManager()->getStorage('linkit_profile')->loadMultiple(); + + $options = []; + foreach ($linkit_profiles as $linkit_profile) { + $options[$linkit_profile->id()] = $linkit_profile->label(); + } + + $elements['linkit_profile'] = [ + '#type' => 'select', + '#title' => $this->t('Linkit profile'), + '#options' => $options, + '#default_value' => $this->getSetting('linkit_profile'), + ]; + + return $elements; + } + + /** + * {@inheritdoc} + */ + public function settingsSummary() { + $summary = parent::settingsSummary(); + + $linkit_profile_id = $this->getSetting('linkit_profile'); + $linkit_profile = \Drupal::entityTypeManager()->getStorage('linkit_profile')->load($linkit_profile_id); + + if ($linkit_profile) { + $summary[] = $this->t('Linkit profile: @linkit_profile', ['@linkit_profile' => $linkit_profile->label()]); + } + + return $summary; + } + + /** + * {@inheritdoc} + */ + public function massageFormValues(array $values, array $form, FormStateInterface $form_state) { + foreach ($values as &$value) { + $value['uri'] = LinkitHelper::uriFromUserInput($value['uri']); + $value += ['options' => []]; + } + return $values; + } + +} diff --git a/web/modules/linkit/src/Plugin/Linkit/Matcher/EmailMatcher.php b/web/modules/linkit/src/Plugin/Linkit/Matcher/EmailMatcher.php index c9513d94d0..d6ea361306 100644 --- a/web/modules/linkit/src/Plugin/Linkit/Matcher/EmailMatcher.php +++ b/web/modules/linkit/src/Plugin/Linkit/Matcher/EmailMatcher.php @@ -23,11 +23,14 @@ class EmailMatcher extends MatcherBase { public function execute($string) { $suggestions = new SuggestionCollection(); + // Strip the mailto: prefix to match only the e-mail part of the string. + $string = str_replace('mailto:', '', $string); + // Check for an e-mail address then return an e-mail match and create a // mail-to link if appropriate. if (filter_var($string, FILTER_VALIDATE_EMAIL)) { $suggestion = new DescriptionSuggestion(); - $suggestion->setLabel($this->t('E-mail @email', ['@email' => $string])) + $suggestion->setLabel($string) ->setPath('mailto:' . Html::escape($string)) ->setGroup($this->t('E-mail')) ->setDescription($this->t('Opens your mail client ready to e-mail @email', ['@email' => $string])); diff --git a/web/modules/linkit/src/Plugin/Linkit/Matcher/EntityMatcher.php b/web/modules/linkit/src/Plugin/Linkit/Matcher/EntityMatcher.php index 9d56a479b6..84c9a44bba 100644 --- a/web/modules/linkit/src/Plugin/Linkit/Matcher/EntityMatcher.php +++ b/web/modules/linkit/src/Plugin/Linkit/Matcher/EntityMatcher.php @@ -344,6 +344,12 @@ public function execute($string) { $entity = $this->entityRepository->getTranslationFromContext($entity); $suggestion = $this->createSuggestion($entity); + if ($query = parse_url($string, PHP_URL_QUERY)) { + $suggestion->setPath($suggestion->getPath() . '?' . $query); + } + if ($fragment = parse_url($string, PHP_URL_FRAGMENT)) { + $suggestion->setPath($suggestion->getPath() . '#' . $fragment); + } $suggestions->addSuggestion($suggestion); } diff --git a/web/modules/linkit/src/Plugin/Linkit/Matcher/FrontPageMatcher.php b/web/modules/linkit/src/Plugin/Linkit/Matcher/FrontPageMatcher.php index dd3ca15eaa..90347b6ed6 100644 --- a/web/modules/linkit/src/Plugin/Linkit/Matcher/FrontPageMatcher.php +++ b/web/modules/linkit/src/Plugin/Linkit/Matcher/FrontPageMatcher.php @@ -2,10 +2,10 @@ namespace Drupal\linkit\Plugin\Linkit\Matcher; -use Drupal\Core\Url; use Drupal\linkit\MatcherBase; use Drupal\linkit\Suggestion\DescriptionSuggestion; use Drupal\linkit\Suggestion\SuggestionCollection; +use Drupal\linkit\Utility\LinkitHelper; /** * Provides specific linkit matchers for the front page. @@ -22,12 +22,18 @@ class FrontPageMatcher extends MatcherBase { */ public function execute($string) { $suggestions = new SuggestionCollection(); + $front_path = '/'; + $query_and_fragment = LinkitHelper::getQueryAndFragment($string); + + if (!empty($query_and_fragment)) { + $string = substr($string, 0, strpos($string, $query_and_fragment)); + } // Special for link to front page. - if (strpos($string, 'front') !== FALSE) { + if (strpos($string, 'front') !== FALSE || $string == $front_path) { $suggestion = new DescriptionSuggestion(); $suggestion->setLabel($this->t('Front page')) - ->setPath(Url::fromRoute('<front>')->toString()) + ->setPath($front_path . $query_and_fragment) ->setGroup($this->t('System')) ->setDescription($this->t('The front page for this site.')); diff --git a/web/modules/linkit/src/ProfileInterface.php b/web/modules/linkit/src/ProfileInterface.php index de8c5da84d..3d2e8cd014 100644 --- a/web/modules/linkit/src/ProfileInterface.php +++ b/web/modules/linkit/src/ProfileInterface.php @@ -38,6 +38,17 @@ public function setDescription($description); */ public function getMatcher($instance_id); + /** + * Returns the first enabled matcher for the given entity type ID. + * + * @param string $entity_type_id + * The entity type ID. + * + * @return \Drupal\linkit\Plugin\Linkit\Matcher\EntityMatcher|null + * An entity matcher instance or null if not found. + */ + public function getMatcherByEntityType($entity_type_id); + /** * Returns the matchers for this profile. * diff --git a/web/modules/linkit/src/Utility/LinkitHelper.php b/web/modules/linkit/src/Utility/LinkitHelper.php new file mode 100644 index 0000000000..56e9fab5bc --- /dev/null +++ b/web/modules/linkit/src/Utility/LinkitHelper.php @@ -0,0 +1,192 @@ +<?php + +namespace Drupal\linkit\Utility; + +use Drupal\Component\Utility\UrlHelper; +use Drupal\Core\Url; + +/** + * Provides helper to operate on URIs. + */ +class LinkitHelper { + + /** + * Load the entity referenced by an entity scheme uri. + * + * @param string $uri + * An internal uri string representing an entity path, such as + * "entity:node/23". + * + * @return \Drupal\Core\Entity\EntityInterface|null + * The most appropriate translation of the entity that matches the given + * uri, or NULL if could not match any entity. + */ + public static function getEntityFromUri($uri) { + // Stripe out potential query and fragment from the uri. + $uri = strtok(strtok($uri, "?"), "#"); + list($entity_type, $entity_id) = explode('/', substr($uri, 7), 2); + $entity_manager = \Drupal::entityTypeManager(); + if ($entity_manager->getDefinition($entity_type, FALSE)) { + if ($entity = $entity_manager->getStorage($entity_type)->load($entity_id)) { + return \Drupal::service('entity.repository')->getTranslationFromContext($entity); + } + } + + return NULL; + } + + /** + * Returns a processed uri with a proper scheme (if applicable). + * + * Turns the internal links into uri strings. + * + * @param string $input + * The raw (or processed) input. + * + * @return string|null + * The uri string or null if the input is empty. + */ + public static function uriFromUserInput($input) { + if (empty($input)) { + return NULL; + } + + $host = parse_url($input, PHP_URL_HOST); + $scheme = parse_url($input, PHP_URL_SCHEME); + + if ($scheme == 'mailto') { + return $input; + } + + if (UrlHelper::isExternal($input)) { + if (UrlHelper::externalIsLocal($input, \Drupal::request()->getSchemeAndHttpHost())) { + // The link points to this domain. Make it relative to perform an entity + // lookup. + $host_end = strpos($input, $host) + strlen($host); + $input = substr($input, $host_end); + } + else { + // This link is really external. + return $input; + } + } + + // Make sure the URI starts with a slash, otherwise the Url's factory + // methods will throw exceptions. + $starts_with_a_slash = strpos($input, '/') === 0; + $is_front = substr($input, 0, 7) === '<front>'; + if (!$scheme && !$is_front && !$starts_with_a_slash) { + $input = "/$input"; + } + + $entity = self::getEntityFromUserInput($input); + if ($entity) { + return 'entity:' . $entity->getEntityTypeId() . '/' . $entity->id() . static::getQueryAndFragment($input); + } + + // It's a relative link. If it's a file, store it as `base:`. Otherwise it's + // most likely internal. + $public_files_dir = \Drupal::service('stream_wrapper_manager') + ->getViaScheme('public') + ->getDirectoryPath(); + if (strpos($input, "/$public_files_dir") === 0) { + return "base:$input"; + } + else { + return "internal:$input"; + } + } + + /** + * Tries to convert an uri into an entity in multiple ways. + * + * @param string $input + * A uri or a path. + * + * @return \Drupal\Core\Entity\EntityInterface|null + * The entity if found, null otherwise. + */ + public static function getEntityFromUserInput($input) { + $scheme = parse_url($input, PHP_URL_SCHEME); + + // Check if it's an entity URI (e.g. entity:node/1). + if ($scheme === 'entity' && ($entity = static::getEntityFromUri($input))) { + return $entity; + } + + // If not, it can be a path pointing to an entity. + if (!$scheme) { + // Which can be hidden behind an alias in any of the site's languages. + $input = 'internal:' . static::getPathByAlias($input); + } + + try { + $route_name = Url::fromUri($input)->getRouteName(); + if ($route_name != "entity.node.edit_form" && $route_name != "node.add") { + $params = Url::fromUri($input)->getRouteParameters(); + $possibly_an_entity_type = key($params); + $entity = \Drupal::entityTypeManager() + ->getStorage($possibly_an_entity_type) + ->load($params[$possibly_an_entity_type]); + return \Drupal::service('entity.repository') + ->getTranslationFromContext($entity); + } + } + catch (\Exception $e) { + // Or not. + } + + return NULL; + } + + /** + * Tries to translate the given raw url path into an internal one. + * + * @param string $input + * Raw URL string consisting of a path and, optionally, query and fragment. + * + * @return string + * The internal path if any matched. The input string otherwise. + */ + public static function getPathByAlias($input) { + $prefixes = \Drupal::config('language.negotiation')->get('url.prefixes'); + /** @var \Drupal\Core\Path\AliasManagerInterface $path_alias_manager */ + $path_alias_manager = \Drupal::service('path.alias_manager'); + /** @var \Drupal\Core\Language\LanguageManagerInterface $language_manager */ + $language_manager = \Drupal::service('language_manager'); + + foreach ($language_manager->getLanguages() as $language) { + $prefix = $prefixes[$language->getId()]; + // Strip the language prefix. + $initial_path = parse_url($input, PHP_URL_PATH); + $path_without_prefix = preg_replace("/^\/$prefix\//", '/', $initial_path); + $path_resolved = $path_alias_manager->getPathByAlias($path_without_prefix, $language->getId()); + if ($path_resolved !== $path_without_prefix) { + return $path_resolved . static::getQueryAndFragment($input); + } + } + + return $input; + } + + /** + * Returns the query and fragment part of a given URL string. + * + * @param string $input + * An arbitrary URL. + * + * @return string + * The query and fragment parts or an empty string. + */ + public static function getQueryAndFragment($input) { + $result = ''; + if ($query = parse_url($input, PHP_URL_QUERY)) { + $result .= "?$query"; + } + if ($fragment = parse_url($input, PHP_URL_FRAGMENT)) { + $result .= "#$fragment"; + } + return $result; + } + +} diff --git a/web/modules/linkit/tests/src/FunctionalJavascript/LinkFieldTest.php b/web/modules/linkit/tests/src/FunctionalJavascript/LinkFieldTest.php new file mode 100644 index 0000000000..de5343892e --- /dev/null +++ b/web/modules/linkit/tests/src/FunctionalJavascript/LinkFieldTest.php @@ -0,0 +1,334 @@ +<?php + +namespace Drupal\Tests\linkit\FunctionalJavascript; + +use Drupal\entity_test\Entity\EntityTestMul; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; +use Drupal\FunctionalJavascriptTests\WebDriverTestBase; +use Drupal\linkit\Tests\ProfileCreationTrait; +use Drupal\node\Entity\NodeType; + +/** + * Tests the widget and formatter for Link fields. + * + * @group linkit + */ +class LinkFieldTest extends WebDriverTestBase { + + use ProfileCreationTrait; + + /** + * {@inheritdoc} + */ + public static $modules = [ + 'node', + 'language', + 'field_ui', + 'entity_test', + 'link', + 'linkit', + ]; + + /** + * A linkit profile. + * + * @var \Drupal\linkit\ProfileInterface + */ + protected $linkitProfile; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $matcherManager = $this->container->get('plugin.manager.linkit.matcher'); + /** @var \Drupal\linkit\MatcherInterface $plugin */ + + $this->linkitProfile = $this->createProfile(); + $plugin = $matcherManager->createInstance('entity:entity_test_mul'); + $this->linkitProfile->addMatcher($plugin->getConfiguration()); + $plugin = $matcherManager->createInstance('email'); + $this->linkitProfile->addMatcher($plugin->getConfiguration()); + $plugin = $matcherManager->createInstance('front_page'); + $this->linkitProfile->addMatcher($plugin->getConfiguration()); + $this->linkitProfile->save(); + + // Create a node type for testing. + NodeType::create(['type' => 'page', 'name' => 'page'])->save(); + + // Create a link field. + $storage = FieldStorageConfig::create([ + 'field_name' => 'field_test_link', + 'entity_type' => 'node', + 'type' => 'link', + ]); + $storage->save(); + FieldConfig::create([ + 'bundle' => 'page', + 'entity_type' => 'node', + 'field_name' => 'field_test_link', + ])->save(); + + // Define our widget and formatter for this field. + entity_get_form_display('node', 'page', 'default') + ->setComponent('field_test_link', [ + 'type' => 'linkit', + ]) + ->save(); + entity_get_display('node', 'page', 'default') + ->setComponent('field_test_link', [ + 'type' => 'linkit', + ]) + ->save(); + + $account = $this->drupalCreateUser([ + 'administer node fields', + 'administer node display', + 'administer nodes', + 'bypass node access', + 'view test entity', + ]); + + $this->drupalLogin($account); + } + + /** + * Test the "linkit" widget and formatter. + */ + public function testLinkFieldWidgetAndFormatter() { + $session = $this->getSession(); + $assert_session = $this->assertSession(); + $page = $session->getPage(); + + // Create a test entity to be used as target. + /** @var \Drupal\Core\Entity\EntityInterface $entity */ + $entity = EntityTestMul::create(['name' => 'Foo']); + $entity->save(); + + // Test the widget behavior. + $this->drupalGet('node/add/page'); + + $assert_session->elementContains('css', '#edit-field-test-link-wrapper', 'Start typing to find content or paste a URL and click on the suggestion below.'); + $widget_wrapper = $assert_session->elementExists('css', '#edit-field-test-link-wrapper'); + $uri_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $uri_input->setValue('f'); + $session->getDriver()->keyDown($uri_input->getXpath(), ' '); + $assert_session->waitOnAutocomplete(); + + // With the default profile no results are found. + $autocomplete_results_wrapper = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete'); + $this->assertTrue($autocomplete_results_wrapper->isVisible()); + $result_description = $assert_session->elementExists('css', 'li.linkit-result-line .linkit-result-line--description', $autocomplete_results_wrapper); + $this->assertEquals('Linkit could not find any suggestions. This URL will be used as is.', $result_description->getText()); + + // Set the widget to use our profile and try again. + entity_get_form_display('node', 'page', 'default') + ->setComponent('field_test_link', [ + 'type' => 'linkit', + 'settings' => [ + 'linkit_profile' => $this->linkitProfile->id(), + ], + ]) + ->save(); + $this->drupalGet('node/add/page'); + $widget_wrapper = $assert_session->elementExists('css', '#edit-field-test-link-wrapper'); + $uri_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $uri_input->setValue('f'); + $session->getDriver()->keyDown($uri_input->getXpath(), 'o'); + $assert_session->waitOnAutocomplete(); + $first_result = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete li.linkit-result-line span.linkit-result-line--title'); + $first_result->click(); + $assert_session->assertWaitOnAjaxRequest(); + + // Check that the URL input field value shows the entity path. + $url_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $this->assertEquals($entity->toUrl()->toString(), $url_input->getValue()); + // Check that the title was populated automatically. + $title_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][title]"]', $widget_wrapper); + $this->assertEquals('Foo', $title_input->getValue()); + + // Give the node a title and save the page. + $page->fillField('title[0][value]', 'Host test node 1'); + $page->pressButton('Save'); + $assert_session->pageTextContains('Host test node 1 has been created'); + + // Check that we are viewing the node, and the formatter displays what we + // expect. + $field_wrapper = $assert_session->elementExists('css', '.field--type-link.field--name-field-test-link'); + $link_element = $assert_session->elementExists('css', 'a', $field_wrapper); + $this->assertEquals('Foo', $link_element->getText()); + $href_value = $link_element->getAttribute('href'); + $this->assertContains("/entity_test_mul/manage/{$entity->id()}", $href_value); + + // Test internal entity targets with anchors. + /** @var \Drupal\Core\Entity\EntityInterface $entity */ + $entity2 = EntityTestMul::create(['name' => 'Anchored Entity']); + $entity2->save(); + + // Test the widget behavior. + $this->drupalGet('node/add/page'); + + $widget_wrapper = $assert_session->elementExists('css', '#edit-field-test-link-wrapper'); + $uri_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $uri_input->setValue('Anchored'); + $session->getDriver()->keyDown($uri_input->getXpath(), ' '); + $assert_session->waitOnAutocomplete(); + $first_result = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete li.linkit-result-line span.linkit-result-line--title'); + $first_result->click(); + $assert_session->assertWaitOnAjaxRequest(); + + // Check that the URL input field value shows the entity path. + $url_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $this->assertEquals($entity2->toUrl()->toString(), $url_input->getValue()); + // Check that the title was populated automatically. + $title_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][title]"]', $widget_wrapper); + $this->assertEquals('Anchored Entity', $title_input->getValue()); + + // Add an anchor to the URL field. + $url_input->setValue($entity2->toUrl()->toString() . '#with-anchor'); + + // Give the node a title and save the page. + $page->fillField('title[0][value]', 'Host test node 2'); + $page->pressButton('Save'); + $assert_session->pageTextContains('Host test node 2 has been created'); + + // Check that we are viewing the node, and the formatter displays what we + // expect. + $field_wrapper = $assert_session->elementExists('css', '.field--type-link.field--name-field-test-link'); + $link_element = $assert_session->elementExists('css', 'a', $field_wrapper); + $this->assertEquals('Anchored Entity', $link_element->getText()); + $href_value = $link_element->getAttribute('href'); + $this->assertContains("/entity_test_mul/manage/{$entity2->id()}#with-anchor", $href_value); + + // Test external URLs. + $this->drupalGet('node/add/page'); + + $widget_wrapper = $assert_session->elementExists('css', '#edit-field-test-link-wrapper'); + $uri_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $uri_input->setValue('https://google.com#foobar'); + $session->getDriver()->keyDown($uri_input->getXpath(), ' '); + $assert_session->waitOnAutocomplete(); + $autocomplete_results_wrapper = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete'); + $this->assertTrue($autocomplete_results_wrapper->isVisible()); + $result_description = $assert_session->elementExists('css', 'li.linkit-result-line .linkit-result-line--description', $autocomplete_results_wrapper); + $this->assertEquals('Linkit could not find any suggestions. This URL will be used as is.', $result_description->getText()); + $first_result = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete li.linkit-result-line span.linkit-result-line--title'); + $first_result->click(); + $assert_session->assertWaitOnAjaxRequest(); + + // Set a manual value for the title. + $title_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][title]"]', $widget_wrapper); + $title_input->setValue('This is google'); + + // Give the node a title and save the page. + $page->fillField('title[0][value]', 'Host test node 3'); + $page->pressButton('Save'); + $assert_session->pageTextContains('Host test node 3 has been created'); + + // Check that we are viewing the node, and the formatter displays what we + // expect. + $field_wrapper = $assert_session->elementExists('css', '.field--type-link.field--name-field-test-link'); + $link_element = $assert_session->elementExists('css', 'a', $field_wrapper); + $this->assertEquals('This is google', $link_element->getText()); + $href_value = $link_element->getAttribute('href'); + $this->assertContains('https://google.com#foobar', $href_value); + + // Test emails. + $this->drupalGet('node/add/page'); + + $email = 'drupal@example.com'; + + $widget_wrapper = $assert_session->elementExists('css', '#edit-field-test-link-wrapper'); + $uri_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $uri_input->setValue($email); + $session->getDriver()->keyDown($uri_input->getXpath(), ' '); + $assert_session->waitOnAutocomplete(); + $first_result = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete li.linkit-result-line span.linkit-result-line--title'); + $first_result->click(); + $assert_session->assertWaitOnAjaxRequest(); + + $url_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $this->assertEquals('mailto:' . $email, $url_input->getValue()); + // Check that the title was populated automatically. + $title_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][title]"]', $widget_wrapper); + $this->assertEquals($email, $title_input->getValue()); + + // Give the node a title and save the page. + $page->fillField('title[0][value]', 'Host test node 4'); + $page->pressButton('Save'); + $assert_session->pageTextContains('Host test node 4 has been created'); + + // Check that we are viewing the node, and the formatter displays what we + // expect. + $field_wrapper = $assert_session->elementExists('css', '.field--type-link.field--name-field-test-link'); + $link_element = $assert_session->elementExists('css', 'a', $field_wrapper); + $this->assertEquals($email, $link_element->getText()); + $href_value = $link_element->getAttribute('href'); + $this->assertContains('mailto:' . $email, $href_value); + + // Test internal host. + $this->drupalGet('node/add/page'); + + $url = \Drupal::request()->getSchemeAndHttpHost() . '/node/1'; + + $widget_wrapper = $assert_session->elementExists('css', '#edit-field-test-link-wrapper'); + $uri_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $uri_input->setValue($url); + $session->getDriver()->keyDown($uri_input->getXpath(), ' '); + $assert_session->waitOnAutocomplete(); + $first_result = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete li.linkit-result-line span.linkit-result-line--title'); + $first_result->click(); + $assert_session->assertWaitOnAjaxRequest(); + + $url_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $this->assertEquals($url, $url_input->getValue()); + // Check that the title was populated automatically. + $title_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][title]"]', $widget_wrapper); + $this->assertEquals($url, $title_input->getValue()); + // Give the node a title and save the page. + $page->fillField('title[0][value]', 'Host test node 5'); + $page->pressButton('Save'); + $assert_session->pageTextContains('Host test node 5 has been created'); + + // Check that we are viewing the node, and the formatter displays what we + // expect Should display the relative url without the host. + $field_wrapper = $assert_session->elementExists('css', '.field--type-link.field--name-field-test-link'); + $link_element = $assert_session->elementExists('css', 'a', $field_wrapper); + $this->assertEquals($url, $link_element->getText()); + $href_value = $link_element->getAttribute('href'); + $this->assertContains('/node/1', $href_value); + + // Test front page. + $this->drupalGet('node/add/page'); + + $widget_wrapper = $assert_session->elementExists('css', '#edit-field-test-link-wrapper'); + $uri_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $uri_input->setValue('<front>'); + $session->getDriver()->keyDown($uri_input->getXpath(), ' '); + $assert_session->waitOnAutocomplete(); + $first_result = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete li.linkit-result-line span.linkit-result-line--title'); + $first_result->click(); + $assert_session->assertWaitOnAjaxRequest(); + + $url_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + + $this->assertEquals('/', $url_input->getValue()); + // Check that the title was populated automatically. + $title_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][title]"]', $widget_wrapper); + $this->assertEquals('Front page', $title_input->getValue()); + + // Give the node a title and save the page. + $page->fillField('title[0][value]', 'Host test node 6'); + $page->pressButton('Save'); + $assert_session->pageTextContains('Host test node 6 has been created'); + + $field_wrapper = $assert_session->elementExists('css', '.field--type-link.field--name-field-test-link'); + $link_element = $assert_session->elementExists('css', 'a', $field_wrapper); + $this->assertEquals('Front page', $link_element->getText()); + $href_value = $link_element->getAttribute('href'); + $this->assertContains('/', $href_value); + } + +} diff --git a/web/modules/linkit/tests/src/FunctionalJavascript/LinkitDialogTest.php b/web/modules/linkit/tests/src/FunctionalJavascript/LinkitDialogTest.php index 7d5f8ce322..8d9ad93ff2 100644 --- a/web/modules/linkit/tests/src/FunctionalJavascript/LinkitDialogTest.php +++ b/web/modules/linkit/tests/src/FunctionalJavascript/LinkitDialogTest.php @@ -191,9 +191,6 @@ public function testLinkDialog() { // Find the first result and click it. $page->find('xpath', '//li[contains(@class, "linkit-result-line") and contains(@class, "ui-menu-item")][1]')->click(); - // Make sure the linkit field field is populated with the node url. - $this->assertEquals($entity->toUrl()->toString(), $href_field->getValue(), 'The href field is populated with the node url.'); - // Make sure all other fields are populated. $this->assertEqualsWithJs('attributes[data-entity-type]', $entity->getEntityTypeId()); $this->assertEqualsWithJs('attributes[data-entity-uuid]', $entity->uuid()); diff --git a/web/modules/linkit/tests/src/Kernel/LinkitAutocompleteTest.php b/web/modules/linkit/tests/src/Kernel/LinkitAutocompleteTest.php index df6c2abb1b..c42721bb4c 100644 --- a/web/modules/linkit/tests/src/Kernel/LinkitAutocompleteTest.php +++ b/web/modules/linkit/tests/src/Kernel/LinkitAutocompleteTest.php @@ -2,7 +2,6 @@ namespace Drupal\Tests\linkit\Kernel; -use Drupal\Component\Render\FormattableMarkup; use Drupal\Component\Serialization\Json; use Drupal\Component\Utility\Html; use Drupal\entity_test\Entity\EntityTest; @@ -110,7 +109,7 @@ public function testAutocompletionEmail() { $email = 'drupal@example.com'; $data = $this->getAutocompleteResult($email); - $this->assertSame((string) new FormattableMarkup('E-mail @email', ['@email' => $email]), $data[0]['label'], 'Autocomplete returned email suggestion.'); + $this->assertSame($email, $data[0]['label'], 'Autocomplete returned email suggestion.'); $this->assertSame('mailto:' . $email, $data[0]['path'], 'Autocomplete returned email suggestion with an mailto href.'); } diff --git a/web/modules/linkit/tests/src/Kernel/LinkitEditorLinkDialogTest.php b/web/modules/linkit/tests/src/Kernel/LinkitEditorLinkDialogTest.php index 0a1963b44e..42504fcdb6 100644 --- a/web/modules/linkit/tests/src/Kernel/LinkitEditorLinkDialogTest.php +++ b/web/modules/linkit/tests/src/Kernel/LinkitEditorLinkDialogTest.php @@ -128,7 +128,7 @@ public function testAdd() { $form_state->setValue(['attributes', 'href'], 'https://example.com/'); $form_state->setValue('href_dirty_check', ''); - $form_state->setValue(['attributes', 'data-entity-type'], $this->randomString()); + $form_state->setValue(['attributes', 'data-entity-type'], $entity->getEntityTypeId()); $form_state->setValue(['attributes', 'data-entity-uuid'], $this->randomString()); $form_state->setValue(['attributes', 'data-entity-substitution'], $this->randomString()); $form_builder->submitForm($form_object, $form_state); -- GitLab