diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php index 33f91fc..604d3d6 100644 --- a/core/lib/Drupal/Core/Url.php +++ b/core/lib/Drupal/Core/Url.php @@ -169,7 +169,7 @@ protected function setExternal() { $this->path = $this->routeName; // Set empty route name and parameters. - $this->routeName = ''; + $this->routeName = NULL; $this->routeParameters = array(); return $this; @@ -291,6 +291,23 @@ public function setOption($name, $value) { } /** + * Returns the external path of the URL. + * + * Only to be used if self::$external is TRUE. + * + * @return string + * The external path. + * + * @throws \Exception + */ + public function getPath() { + if (!$this->isExternal()) { + throw new \Exception('Internal URLs do not have external paths.'); + } + return $this->path; + } + + /** * Sets the absolute value for this Url. * * @param bool $absolute @@ -308,7 +325,7 @@ public function setAbsolute($absolute = TRUE) { */ public function toString() { if ($this->isExternal()) { - return $this->urlGenerator()->generateFromPath($this->path, $this->getOptions()); + return $this->urlGenerator()->generateFromPath($this->getPath(), $this->getOptions()); } return $this->urlGenerator()->generateFromRoute($this->getRouteName(), $this->getRouteParameters(), $this->getOptions()); @@ -321,11 +338,19 @@ public function toString() { * An associative array containing all the properties of the route. */ public function toArray() { - return array( - 'route_name' => $this->getRouteName(), - 'route_parameters' => $this->getRouteParameters(), - 'options' => $this->getOptions(), - ); + if ($this->isExternal()) { + return array( + 'path' => $this->getPath(), + 'options' => $this->getOptions(), + ); + } + else { + return array( + 'route_name' => $this->getRouteName(), + 'route_parameters' => $this->getRouteParameters(), + 'options' => $this->getOptions(), + ); + } } /** @@ -335,11 +360,19 @@ public function toArray() { * An associative array suitable for a render array. */ public function toRenderArray() { - return array( - '#route_name' => $this->getRouteName(), - '#route_parameters' => $this->getRouteParameters(), - '#options' => $this->getOptions(), - ); + if ($this->isExternal()) { + return array( + '#href' => $this->getPath(), + '#options' => $this->getOptions(), + ); + } + else { + return array( + '#route_name' => $this->getRouteName(), + '#route_parameters' => $this->getRouteParameters(), + '#options' => $this->getOptions(), + ); + } } /** diff --git a/core/modules/link/lib/Drupal/link/LinkItemInterface.php b/core/modules/link/lib/Drupal/link/LinkItemInterface.php new file mode 100644 index 0000000..afe6cb7 --- /dev/null +++ b/core/modules/link/lib/Drupal/link/LinkItemInterface.php @@ -0,0 +1,40 @@ + $item) { // By default use the full URL as the link text. - $link_title = $item->url; + $url = $this->buildUrl($item); + $link_title = $url->toString(); // If the title field value is available, use it for the link text. if (empty($settings['url_only']) && !empty($item->title)) { @@ -142,13 +143,10 @@ public function viewElements(FieldItemListInterface $items) { ); } else { - $link = $this->buildLink($item); $element[$delta] = array( '#type' => 'link', '#title' => $link_title, - '#href' => $link['path'], - '#options' => $link['options'], - ); + ) + $url->toRenderArray(); } } @@ -156,41 +154,36 @@ public function viewElements(FieldItemListInterface $items) { } /** - * Builds the link information for a link field item. + * Builds the \Drupal\Core\Url object for a link field item. * - * @param \Drupal\Core\Field\FieldItemInterface $item + * @param \Drupal\link\LinkItemInterface $item * The link field item being rendered. * - * @return array - * An array with the following key/value pairs: - * - 'path': a string suitable for the $path parameter in l(). - * - 'options': an array suitable for the $options parameter in l(). + * @return \Drupal\Core\Url + * An Url object. */ - protected function buildLink(FieldItemInterface $item) { + protected function buildUrl(LinkItemInterface $item) { $settings = $this->getSettings(); - - // Split out the link into the parts required for url(): path and options. - $parsed_url = Url::parse($item->url); - $result = array( - 'path' => $parsed_url['path'], - 'options' => array( - 'query' => $parsed_url['query'], - 'fragment' => $parsed_url['fragment'], - 'attributes' => $item->attributes, - ), - ); + $options = $item->options; // Add optional 'rel' attribute to link options. if (!empty($settings['rel'])) { - $result['options']['attributes']['rel'] = $settings['rel']; + $options['attributes']['rel'] = $settings['rel']; } // Add optional 'target' attribute to link options. if (!empty($settings['target'])) { - $result['options']['attributes']['target'] = $settings['target']; + $options['attributes']['target'] = $settings['target']; } - return $result; + if ($item->isExternal()) { + $url = Url::createFromPath($item->url); + $url->setOptions($options); + } + else { + $url = new Url($item->route_name, (array) $item->route_parameters, (array) $options); + } + + return $url; } } - diff --git a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkSeparateFormatter.php b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkSeparateFormatter.php index 4392123..9ac9282 100644 --- a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkSeparateFormatter.php +++ b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkSeparateFormatter.php @@ -42,7 +42,8 @@ public function viewElements(FieldItemListInterface $items) { foreach ($items as $delta => $item) { // By default use the full URL as the link text. - $link_title = $item->url; + $url = $this->buildUrl($item); + $link_title = $url->toString(); // If the link text field value is available, use it for the text. if (empty($settings['url_only']) && !empty($item->title)) { @@ -58,19 +59,17 @@ public function viewElements(FieldItemListInterface $items) { if (empty($item->title)) { $link_title = NULL; } - $url_title = $item->url; + $url_title = $url->toString(); if (!empty($settings['trim_length'])) { $link_title = truncate_utf8($link_title, $settings['trim_length'], FALSE, TRUE); - $url_title = truncate_utf8($item->url, $settings['trim_length'], FALSE, TRUE); + $url_title = truncate_utf8($url_title, $settings['trim_length'], FALSE, TRUE); } - $link = $this->buildLink($item); $element[$delta] = array( '#theme' => 'link_formatter_link_separate', '#title' => $link_title, '#url_title' => $url_title, - '#href' => $link['path'], - '#options' => $link['options'], + '#url' => $url, ); } return $element; diff --git a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php index 17853c3..105a358 100644 --- a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php +++ b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php @@ -7,10 +7,12 @@ namespace Drupal\link\Plugin\Field\FieldType; +use Drupal\Component\Utility\Url as UrlHelper; use Drupal\Core\Field\ConfigFieldItemBase; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\TypedData\DataDefinition; use Drupal\Core\TypedData\MapDataDefinition; +use Drupal\link\LinkItemInterface; /** * Plugin implementation of the 'link' field type. @@ -18,28 +20,35 @@ * @FieldType( * id = "link", * label = @Translation("Link"), - * description = @Translation("Stores a URL string, optional varchar link text, and optional blob of attributes to assemble a link."), + * description = @Translation("Stores a URL string, optional varchar link text, and optional blob of options to assemble a link."), * instance_settings = { + * "url_type" = \Drupal\link\LinkItemInterface::LINK_GENERIC, * "title" = "1" * }, * default_widget = "link_default", * default_formatter = "link" * ) */ -class LinkItem extends ConfigFieldItemBase { +class LinkItem extends ConfigFieldItemBase implements LinkItemInterface { /** * {@inheritdoc} */ public static function propertyDefinitions(FieldDefinitionInterface $field_definition) { - $properties['url'] = DataDefinition::create('uri') + $properties['url'] = DataDefinition::create('string') ->setLabel(t('URL')); $properties['title'] = DataDefinition::create('string') ->setLabel(t('Link text')); - $properties['attributes'] = MapDataDefinition::create() - ->setLabel(t('Attributes')); + $properties['route_name'] = DataDefinition::create('string') + ->setLabel(t('Route name')); + + $properties['route_parameters'] = MapDataDefinition::create() + ->setLabel(t('Route parameters')); + + $properties['options'] = MapDataDefinition::create() + ->setLabel(t('Options')); return $properties; } @@ -62,8 +71,21 @@ public static function schema(FieldDefinitionInterface $field_definition) { 'length' => 255, 'not null' => FALSE, ), - 'attributes' => array( - 'description' => 'Serialized array of attributes for the link.', + 'route_name' => array( + 'description' => 'The machine name of a defined Route this link represents.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ), + 'route_parameters' => array( + 'description' => 'Serialized array of route parameters of the link.', + 'type' => 'blob', + 'size' => 'big', + 'not null' => FALSE, + 'serialize' => TRUE, + ), + 'options' => array( + 'description' => 'Serialized array of options for the link.', 'type' => 'blob', 'size' => 'big', 'not null' => FALSE, @@ -79,6 +101,17 @@ public static function schema(FieldDefinitionInterface $field_definition) { public function instanceSettingsForm(array $form, array &$form_state) { $element = array(); + $element['url_type'] = array( + '#type' => 'radios', + '#title' => t('Allowed url type'), + '#default_value' => $this->getSetting('url_type'), + '#options' => array( + static::LINK_INTERNAL => t('Internal urls only'), + static::LINK_EXTERNAL => t('External urls only'), + static::LINK_GENERIC => t('Both internal and external urls'), + ), + ); + $element['title'] = array( '#type' => 'radios', '#title' => t('Allow link text'), @@ -97,9 +130,16 @@ public function instanceSettingsForm(array $form, array &$form_state) { * {@inheritdoc} */ public function preSave() { - // Trim any spaces around the URL and link text. - $this->url = trim($this->url); + // Trim any spaces around the link text. $this->title = trim($this->title); + + // Split out the link 'query' and 'fragment' parts. + $parsed_url = UrlHelper::parse(trim($this->url)); + $this->url = $parsed_url['path']; + $this->options = array( + 'query' => $parsed_url['query'], + 'fragment' => $parsed_url['fragment'], + ) + $this->options; } /** @@ -110,4 +150,12 @@ public function isEmpty() { return $value === NULL || $value === ''; } + /** + * {@inheritdoc} + */ + public function isExternal() { + // External URLs don't have a route_name value. + return empty($this->route_name); + } + } diff --git a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldWidget/LinkWidget.php b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldWidget/LinkWidget.php index dc10574..9cbede3 100644 --- a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldWidget/LinkWidget.php +++ b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldWidget/LinkWidget.php @@ -7,8 +7,14 @@ namespace Drupal\link\Plugin\Field\FieldWidget; +use Drupal\Component\Utility\Url as UrlHelper; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\WidgetBase; +use Drupal\Core\ParamConverter\ParamNotConvertedException; +use Drupal\Core\Routing\MatchingRouteNotFoundException; +use Drupal\Core\Url; +use Drupal\link\LinkItemInterface; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Plugin implementation of the 'link' widget. @@ -31,17 +37,44 @@ class LinkWidget extends WidgetBase { * {@inheritdoc} */ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, array &$form_state) { + $url_type = $this->getFieldSetting('url_type'); + + $default_url_value = NULL; + if (isset($items[$delta]->url)) { + $url = Url::createFromPath($items[$delta]->url); + $url->setOptions($items[$delta]->options); + $default_url_value = ltrim($url->toString(), '/'); + } $element['url'] = array( '#type' => 'url', - '#title' => t('URL'), + '#title' => $this->t('URL'), '#placeholder' => $this->getSetting('placeholder_url'), - '#default_value' => isset($items[$delta]->url) ? $items[$delta]->url : NULL, + '#default_value' => $default_url_value, '#maxlength' => 2048, '#required' => $element['#required'], ); + + // If the field is configured to allow internal paths, it cannot use the + // 'url' form element and we have to do the validation ourselves. + if ($url_type & LinkItemInterface::LINK_INTERNAL) { + $element['url']['#type'] = 'textfield'; + $element['#element_validate'][] = array($this, 'validateUrl'); + } + + // If the field is configured to allow only internal paths, add a useful + // element prefix. + if ($url_type == LinkItemInterface::LINK_INTERNAL) { + $element['url']['#field_prefix'] = \Drupal::url('', array(), array('absolute' => TRUE)); + } + // If the field is configured to allow both internal and external paths, + // show a useful description. + elseif ($url_type == LinkItemInterface::LINK_GENERIC) { + $element['url']['#description'] = $this->t('This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org')); + } + $element['title'] = array( '#type' => 'textfield', - '#title' => t('Link text'), + '#title' => $this->t('Link text'), '#placeholder' => $this->getSetting('placeholder_title'), '#default_value' => isset($items[$delta]->title) ? $items[$delta]->title : NULL, '#maxlength' => 255, @@ -52,7 +85,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen // settings cannot be saved otherwise. $is_field_edit_form = ($element['#entity'] === NULL); if (!$is_field_edit_form && $this->getFieldSetting('title') == DRUPAL_REQUIRED) { - $element['#element_validate'] = array(array($this, 'validateTitle')); + $element['#element_validate'][] = array($this, 'validateTitle'); } // Exposing the attributes array in the widget is left for alternate and more @@ -60,7 +93,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen $element['attributes'] = array( '#type' => 'value', '#tree' => TRUE, - '#value' => !empty($items[$delta]->attributes) ? $items[$delta]->attributes : array(), + '#value' => !empty($items[$delta]->options['attributes']) ? $items[$delta]->options['attributes'] : array(), '#attributes' => array('class' => array('link-field-widget-attributes')), ); @@ -83,15 +116,15 @@ public function settingsForm(array $form, array &$form_state) { $elements['placeholder_url'] = array( '#type' => 'textfield', - '#title' => t('Placeholder for URL'), + '#title' => $this->t('Placeholder for URL'), '#default_value' => $this->getSetting('placeholder_url'), - '#description' => t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'), + '#description' => $this->t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'), ); $elements['placeholder_title'] = array( '#type' => 'textfield', - '#title' => t('Placeholder for link text'), + '#title' => $this->t('Placeholder for link text'), '#default_value' => $this->getSetting('placeholder_title'), - '#description' => t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'), + '#description' => $this->t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'), '#states' => array( 'invisible' => array( ':input[name="instance[settings][title]"]' => array('value' => DRUPAL_DISABLED), @@ -111,31 +144,91 @@ public function settingsSummary() { $placeholder_title = $this->getSetting('placeholder_title'); $placeholder_url = $this->getSetting('placeholder_url'); if (empty($placeholder_title) && empty($placeholder_url)) { - $summary[] = t('No placeholders'); + $summary[] = $this->t('No placeholders'); } else { if (!empty($placeholder_title)) { - $summary[] = t('Title placeholder: @placeholder_title', array('@placeholder_title' => $placeholder_title)); + $summary[] = $this->t('Title placeholder: @placeholder_title', array('@placeholder_title' => $placeholder_title)); } if (!empty($placeholder_url)) { - $summary[] = t('URL placeholder: @placeholder_url', array('@placeholder_url' => $placeholder_url)); + $summary[] = $this->t('URL placeholder: @placeholder_url', array('@placeholder_url' => $placeholder_url)); } } return $summary; } - /** - * Form element validation handler for link_field_widget_form(). + * Form element validation handler; Validates the title property. * * Conditionally requires the link title if a URL value was filled in. */ - function validateTitle(&$element, &$form_state, $form) { + public function validateTitle(&$element, &$form_state, $form) { if ($element['url']['#value'] !== '' && $element['title']['#value'] === '') { $element['title']['#required'] = TRUE; - form_error($element['title'], $form_state, t('!name field is required.', array('!name' => $element['title']['#title']))); + \Drupal::formBuilder()->setError($element['title'], $form_state, $this->t('!name field is required.', array('!name' => $element['title']['#title']))); } } -} + /** + * Form element validation handler; Validates the url property. + */ + public function validateUrl(&$element, &$form_state, $form) { + $url_type = $this->getFieldSetting('url_type'); + $url_is_valid = TRUE; + + // Validate only if the field type supports internal URLs. + if ($element['url']['#value'] !== '' && $url_type & LinkItemInterface::LINK_INTERNAL) { + try { + $url = Url::createFromPath($element['url']['#value']); + + if ($url->isExternal() && !UrlHelper::isValid($element['url']['#value'], TRUE)) { + $url_is_valid = FALSE; + } + elseif ($url->isExternal() && $url_type == LinkItemInterface::LINK_INTERNAL) { + $url_is_valid = FALSE; + } + } + catch (NotFoundHttpException $e) { + $url_is_valid = FALSE; + } + catch (MatchingRouteNotFoundException $e) { + $url_is_valid = FALSE; + } + catch (ParamNotConvertedException $e) { + $url_is_valid = FALSE; + } + } + + if (!$url_is_valid) { + \Drupal::formBuilder()->setError($element['url'], $form_state, $this->t('The URL %url is not valid.', array('%url' => $element['url']['#value']))); + } + } + + /** + * {@inheritdoc} + */ + public function massageFormValues(array $values, array $form, array &$form_state) { + foreach ($values as &$value) { + if (!empty($value['url'])) { + try { + $url = Url::createFromPath($value['url']); + $url->setOption('attributes', $value['attributes']); + + $value += $url->toArray(); + } + catch (NotFoundHttpException $e) { + // Nothing to do here, validateUrl() emits form validation errors. + } + catch (MatchingRouteNotFoundException $e) { + // Nothing to do here, validateUrl() emits form validation errors. + } + catch (ParamNotConvertedException $e) { + // Nothing to do here, validateUrl() emits form validation errors. + } + } + } + return $values; + } + +} diff --git a/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php b/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php index 1be52d5..d2b0cc7 100644 --- a/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php +++ b/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php @@ -7,8 +7,9 @@ namespace Drupal\link\Tests; -use Drupal\simpletest\WebTestBase; use Drupal\Component\Utility\String; +use Drupal\link\LinkItemInterface; +use Drupal\simpletest\WebTestBase; /** * Tests link field widgets and formatters. @@ -73,14 +74,16 @@ function testURLValidation() { 'type' => 'link', )); $this->field->save(); - entity_create('field_instance_config', array( + $this->instance = entity_create('field_instance_config', array( 'field_name' => $field_name, 'entity_type' => 'entity_test', 'bundle' => 'entity_test', 'settings' => array( 'title' => DRUPAL_DISABLED, + 'url_type' => LinkItemInterface::LINK_GENERIC, ), - ))->save(); + )); + $this->instance->save(); entity_get_form_display('entity_test', 'entity_test', 'default') ->setComponent($field_name, array( 'type' => 'link_default', @@ -100,21 +103,16 @@ function testURLValidation() { $this->assertFieldByName("{$field_name}[0][url]", '', 'Link URL field is displayed'); $this->assertRaw('placeholder="http://example.com"'); - // Verify that a valid URL can be submitted. - $value = 'http://www.example.com/'; - $edit = array( - 'user_id' => 1, - 'name' => $this->randomName(), - "{$field_name}[0][url]" => $value, + // Define some valid URLs. + $valid_external_entries = array( + 'http://www.example.com/', + ); + $valid_internal_entries = array( + 'entity_test/add', ); - $this->drupalPostForm(NULL, $edit, t('Save')); - preg_match('|entity_test/manage/(\d+)|', $this->url, $match); - $id = $match[1]; - $this->assertText(t('entity_test @id has been created.', array('@id' => $id))); - $this->assertRaw($value); - // Verify that invalid URLs cannot be submitted. - $wrong_entries = array( + // Define some invalid URLs. + $invalid_external_entries = array( // Missing protcol 'not-an-url', // Invalid protocol @@ -122,14 +120,66 @@ function testURLValidation() { // Missing host name 'http://', ); - $this->drupalGet('entity_test/add'); - foreach ($wrong_entries as $invalid_value) { + $invalid_internal_entries = array( + 'non/existing/path', + ); + + // Test external and internal URLs for 'url_type' = LinkItemInterface::LINK_GENERIC. + $this->assertValidEntries($field_name, $valid_external_entries + $valid_internal_entries); + $this->assertInvalidEntries($field_name, $invalid_external_entries + $invalid_internal_entries); + + // Test external URLs for 'url_type' = LinkItemInterface::LINK_EXTERNAL. + $this->instance->settings['url_type'] = LinkItemInterface::LINK_EXTERNAL; + $this->instance->save(); + $this->assertValidEntries($field_name, $valid_external_entries); + $this->assertInvalidEntries($field_name, $valid_internal_entries + $invalid_external_entries); + + // Test external URLs for 'url_type' = LinkItemInterface::LINK_INTERNAL. + $this->instance->settings['url_type'] = LinkItemInterface::LINK_INTERNAL; + $this->instance->save(); + $this->assertValidEntries($field_name, $valid_internal_entries); + $this->assertInvalidEntries($field_name, $valid_external_entries + $invalid_internal_entries); + } + + /** + * Asserts that valid URLs can be submitted. + * + * @param string $field_name + * The field name. + * @param array $valid_entries + * An array of valid URL entries. + */ + protected function assertValidEntries($field_name, array $valid_entries) { + foreach ($valid_entries as $value) { + $edit = array( + 'user_id' => 1, + 'name' => $this->randomName(), + "{$field_name}[0][url]" => $value, + ); + $this->drupalPostForm('entity_test/add', $edit, t('Save')); + preg_match('|entity_test/manage/(\d+)|', $this->url, $match); + $id = $match[1]; + $this->assertText(t('entity_test @id has been created.', array('@id' => $id))); + $this->assertRaw($value); + } + } + + /** + * Asserts that invalid URLs cannot be submitted. + * + * @param string $field_name + * The field name. + * @param array $invalid_entries + * An array of invalid URL entries. + */ + protected function assertInvalidEntries($field_name, array $invalid_entries) { + foreach ($invalid_entries as $invalid_value) { $edit = array( 'user_id' => 1, 'name' => $this->randomName(), "{$field_name}[0][url]" => $invalid_value, ); - $this->drupalPostForm(NULL, $edit, t('Save')); + $this->drupalPostForm('entity_test/add', $edit, t('Save')); $this->assertText(t('The URL @url is not valid.', array('@url' => $invalid_value))); } } diff --git a/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php b/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php index d1e4ea6..4279753 100644 --- a/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php +++ b/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php @@ -58,7 +58,7 @@ public function testLinkItem() { $class = $this->randomName(); $entity->field_test->url = $url; $entity->field_test->title = $title; - $entity->field_test->first()->get('attributes')->set('class', $class); + $entity->field_test->first()->get('options')->set('attributes', array('class' => $class)); $entity->name->value = $this->randomName(); $entity->save(); @@ -71,7 +71,7 @@ public function testLinkItem() { $this->assertEqual($entity->field_test[0]->url, $url); $this->assertEqual($entity->field_test->title, $title); $this->assertEqual($entity->field_test[0]->title, $title); - $this->assertEqual($entity->field_test->attributes['class'], $class); + $this->assertEqual($entity->field_test->options['attributes']['class'], $class); // Verify changing the field value. $new_url = 'http://drupal.org'; @@ -79,17 +79,17 @@ public function testLinkItem() { $new_class = $this->randomName(); $entity->field_test->url = $new_url; $entity->field_test->title = $new_title; - $entity->field_test->first()->get('attributes')->set('class', $new_class); + $entity->field_test->first()->get('options')->set('attributes', array('class' => $new_class)); $this->assertEqual($entity->field_test->url, $new_url); $this->assertEqual($entity->field_test->title, $new_title); - $this->assertEqual($entity->field_test->attributes['class'], $new_class); + $this->assertEqual($entity->field_test->options['attributes']['class'], $new_class); // Read changed entity and assert changed values. $entity->save(); $entity = entity_load('entity_test', $id); $this->assertEqual($entity->field_test->url, $new_url); $this->assertEqual($entity->field_test->title, $new_title); - $this->assertEqual($entity->field_test->attributes['class'], $new_class); + $this->assertEqual($entity->field_test->options['attributes']['class'], $new_class); } } diff --git a/core/modules/link/link.module b/core/modules/link/link.module index 0e830df..2c06c5d 100644 --- a/core/modules/link/link.module +++ b/core/modules/link/link.module @@ -15,7 +15,7 @@ function link_help($path, $arg) { case 'admin/help#link': $output = ''; $output .= '

' . t('About') . '

'; - $output .= '

' . t('The Link module allows you to create fields that contain external URLs and optional link text. See the Field module help and the Field UI help pages for general information on fields and how to create and manage them. For more information, see the online documentation for the Link module.', array('!field' => \Drupal::url('help.page', array('name' => 'field')), '!field_ui' => \Drupal::url('help.page', array('name' => 'field_ui')), '!link_documentation' => 'https://drupal.org/documentation/modules/link')) . '

'; + $output .= '

' . t('The Link module allows you to create fields that contain internal or external URLs and optional link text. See the Field module help and the Field UI help pages for general information on fields and how to create and manage them. For more information, see the online documentation for the Link module.', array('!field' => \Drupal::url('help.page', array('name' => 'field')), '!field_ui' => \Drupal::url('help.page', array('name' => 'field_ui')), '!link_documentation' => 'https://drupal.org/documentation/modules/link')) . '

'; $output .= '

' . t('Uses') . '

'; $output .= '
'; $output .= '
' . t('Managing and displaying link fields') . '
'; @@ -27,7 +27,7 @@ function link_help($path, $arg) { $output .= '
' . t('Adding attributes to links') . '
'; $output .= '
' . t('You can add attributes to links, by changing the Format settings in the Manage display page. Adding rel="nofollow" notifies search engines that links should not be followed.') . '
'; $output .= '
' . t('Validating URLs') . '
'; - $output .= '
' . t('All links are validated after a link field is filled in. They can include anchors or query strings. Links need to start with either the scheme name http or https; other scheme names (for example ftp or git) are not supported.') . '
'; + $output .= '
' . t('All links are validated after a link field is filled in. They can include anchors or query strings.') . '
'; $output .= '
'; return $output; } @@ -39,7 +39,7 @@ function link_help($path, $arg) { function link_theme() { return array( 'link_formatter_link_separate' => array( - 'variables' => array('title' => NULL, 'url_title' => NULL, 'href' => NULL, 'options' => array()), + 'variables' => array('title' => NULL, 'url_title' => NULL, 'url' => NULL), 'template' => 'link-formatter-link-separate', ), ); @@ -57,12 +57,17 @@ function link_theme() { * - title: (optional) A descriptive or alternate title for the link, which * may be different than the actual link text. * - url_title: The anchor text for the link. - * - href: The link URL. - * - options: (optional) An array of options to pass to l(). + * - url: A \Drupal\Core\Url object. */ function template_preprocess_link_formatter_link_separate(&$variables) { if (!empty($variables['title'])) { $variables['title'] = String::checkPlain($variables['title']); } - $variables['link'] = l($variables['url_title'], $variables['href'], $variables['options']); + + if (!$variables['url']->isExternal()) { + $variables['link'] = \Drupal::linkGenerator()->generateFromUrl($variables['url_title'], $variables['url']); + } + else { + $variables['link'] = l($variables['url_title'], $variables['url']->getPath(), $variables['url']->getOptions()); + } } diff --git a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php index ac867c3..f355b97 100644 --- a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php +++ b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php @@ -142,8 +142,7 @@ public function testToString(Url $url) { */ public function testToArray(Url $url) { $expected = array( - 'route_name' => '', - 'route_parameters' => array(), + 'path' => $this->path, 'options' => array(), ); $this->assertSame($expected, $url->toArray()); @@ -157,7 +156,7 @@ public function testToArray(Url $url) { * @covers ::getRouteName() */ public function testGetRouteName(Url $url) { - $this->assertSame('', $url->getRouteName()); + $this->assertSame(NULL, $url->getRouteName()); } /** @@ -185,6 +184,17 @@ public function testGetInternalPath(Url $url) { } /** + * Tests the getPath() method. + * + * @depends testCreateFromPath + * + * @covers ::getPath() + */ + public function testGetPath(Url $url) { + $this->assertNotNull($url->getPath()); + } + + /** * Tests the getOptions() method. * * @depends testCreateFromPath diff --git a/core/tests/Drupal/Tests/Core/UrlTest.php b/core/tests/Drupal/Tests/Core/UrlTest.php index dd786dc..50ea4d3 100644 --- a/core/tests/Drupal/Tests/Core/UrlTest.php +++ b/core/tests/Drupal/Tests/Core/UrlTest.php @@ -202,6 +202,21 @@ public function testIsExternal($urls) { } /** + * Tests the getPath() method for internal URLs. + * + * @depends testCreateFromPath + * + * @expectedException \Exception + * + * @covers ::getPath() + */ + public function testGetPathForInternalUrl($urls) { + foreach ($urls as $url) { + $url->getPath(); + } + } + + /** * Tests the toString() method. * * @param \Drupal\Core\Url[] $urls