Commit 17f5f48b authored by alexpott's avatar alexpott

Issue #2411143 by dawehner, effulgentsia, amateescu, larowlan: Change LinkItem...

Issue #2411143 by dawehner, effulgentsia, amateescu, larowlan: Change LinkItem schema to store URIs rather than URLs/paths/routes
parent 91a05649
......@@ -86,7 +86,7 @@ public function testBlockFields() {
$this->drupalGet('block/add/link');
$edit = array(
'info[0][value]' => $this->randomMachineName(8),
$this->fieldStorage->getName() . '[0][url]' => 'http://example.com',
$this->fieldStorage->getName() . '[0][uri]' => 'http://example.com',
$this->fieldStorage->getName() . '[0][title]' => 'Example.com'
);
$this->drupalPostForm(NULL, $edit, t('Save'));
......
......@@ -9,11 +9,15 @@
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Path\PathValidatorInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Url;
use Drupal\link\LinkItemInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
......@@ -27,7 +31,55 @@
* }
* )
*/
class LinkFormatter extends FormatterBase {
class LinkFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
/**
* The path validator service.
*
* @var \Drupal\Core\Path\PathValidatorInterface
*/
protected $pathValidator;
/**
* {@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')
);
}
/**
* Constructs a new LinkFormatter.
*
* @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.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, PathValidatorInterface $path_validator) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->pathValidator = $path_validator;
}
/**
* {@inheritdoc}
......@@ -155,7 +207,7 @@ public function viewElements(FieldItemListInterface $items) {
// Piggyback on the metadata attributes, which will be placed in the
// field template wrapper, and set the URL value in a content
// attribute.
$item->_attributes += array('content' => $item->url);
$item->_attributes += array('content' => $item->uri);
}
}
else {
......@@ -189,6 +241,10 @@ public function viewElements(FieldItemListInterface $items) {
* An Url object.
*/
protected function buildUrl(LinkItemInterface $item) {
// @todo Consider updating the usage of the path validator with whatever
// gets added in https://www.drupal.org/node/2405551.
$url = $this->pathValidator->getUrlIfValidWithoutAccessCheck($item->uri) ?: Url::fromRoute('<none>');
$settings = $this->getSettings();
$options = $item->options;
......@@ -200,13 +256,7 @@ protected function buildUrl(LinkItemInterface $item) {
if (!empty($settings['target'])) {
$options['attributes']['target'] = $settings['target'];
}
if ($item->isExternal()) {
$url = Url::fromUri($item->url, $options);
}
else {
$url = Url::fromRoute($item->route_name, (array) $item->route_parameters, (array) $options);
}
$url->setOptions($options);
return $url;
}
......
......@@ -44,18 +44,14 @@ public static function defaultFieldSettings() {
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['url'] = DataDefinition::create('string')
->setLabel(t('URL'));
// @todo Change the type from 'string' to 'uri':
// https://www.drupal.org/node/2412509.
$properties['uri'] = DataDefinition::create('string')
->setLabel(t('URI'));
$properties['title'] = DataDefinition::create('string')
->setLabel(t('Link text'));
$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'));
......@@ -68,8 +64,8 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'url' => array(
'description' => 'The URL of the link.',
'uri' => array(
'description' => 'The URI of the link.',
'type' => 'varchar',
'length' => 2048,
),
......@@ -78,17 +74,6 @@ public static function schema(FieldStorageDefinitionInterface $field_definition)
'type' => 'varchar',
'length' => 255,
),
'route_name' => array(
'description' => 'The machine name of a defined Route this link represents.',
'type' => 'varchar',
'length' => 255,
),
'route_parameters' => array(
'description' => 'Serialized array of route parameters of the link.',
'type' => 'blob',
'size' => 'big',
'serialize' => TRUE,
),
'options' => array(
'description' => 'Serialized array of options for the link.',
'type' => 'blob',
......@@ -152,7 +137,7 @@ public static function generateSampleValue(FieldDefinitionInterface $field_defin
$values['title'] = mt_rand(0,1) ? $random->sentences(4) : '';
break;
}
$values['url'] = 'http://www.' . $random->word($domain_length) . '.' . $tlds[mt_rand(0, (sizeof($tlds)-1))];
$values['uri'] = 'http://www.' . $random->word($domain_length) . '.' . $tlds[mt_rand(0, (sizeof($tlds)-1))];
return $values;
}
......@@ -160,7 +145,7 @@ public static function generateSampleValue(FieldDefinitionInterface $field_defin
* {@inheritdoc}
*/
public function isEmpty() {
$value = $this->get('url')->getValue();
$value = $this->get('uri')->getValue();
return $value === NULL || $value === '';
}
......@@ -168,14 +153,15 @@ public function isEmpty() {
* {@inheritdoc}
*/
public function isExternal() {
// External links don't have a route_name value.
return empty($this->route_name);
// External links don't resolve to a route.
$url = \Drupal::pathValidator()->getUrlIfValid($this->uri);
return $url->isExternal();
}
/**
* {@inheritdoc}
*/
public static function mainPropertyName() {
return 'url';
return 'uri';
}
}
......@@ -41,13 +41,13 @@ public static function defaultSettings() {
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$default_url_value = NULL;
if (isset($items[$delta]->url)) {
if ($url = \Drupal::pathValidator()->getUrlIfValid($items[$delta]->url)) {
if (isset($items[$delta]->uri)) {
if ($url = \Drupal::pathValidator()->getUrlIfValid($items[$delta]->uri)) {
$url->setOptions($items[$delta]->options);
$default_url_value = ltrim($url->toString(), '/');
}
}
$element['url'] = array(
$element['uri'] = array(
'#type' => 'url',
'#title' => $this->t('URL'),
'#placeholder' => $this->getSetting('placeholder_url'),
......@@ -59,18 +59,18 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
// If the field is configured to support internal links, it cannot use the
// 'url' form element and we have to do the validation ourselves.
if ($this->supportsInternalLinks()) {
$element['url']['#type'] = 'textfield';
$element['uri']['#type'] = 'textfield';
}
// If the field is configured to allow only internal links, add a useful
// element prefix.
if (!$this->supportsExternalLinks()) {
$element['url']['#field_prefix'] = \Drupal::url('<front>', array(), array('absolute' => TRUE));
$element['uri']['#field_prefix'] = \Drupal::url('<front>', array(), array('absolute' => TRUE));
}
// If the field is configured to allow both internal and external links,
// show a useful description.
elseif ($this->supportsExternalLinks() && $this->supportsInternalLinks()) {
$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' => '<front>', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org'));
$element['uri']['#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' => '<front>', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org'));
}
$element['title'] = array(
......@@ -188,7 +188,7 @@ public function settingsSummary() {
* Conditionally requires the link title if a URL value was filled in.
*/
public function validateTitle(&$element, FormStateInterface $form_state, $form) {
if ($element['url']['#value'] !== '' && $element['title']['#value'] === '') {
if ($element['uri']['#value'] !== '' && $element['title']['#value'] === '') {
$element['title']['#required'] = TRUE;
$form_state->setError($element['title'], $this->t('!name field is required.', array('!name' => $element['title']['#title'])));
}
......@@ -199,19 +199,16 @@ public function validateTitle(&$element, FormStateInterface $form_state, $form)
*/
public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
foreach ($values as &$value) {
if (!empty($value['url'])) {
$url = \Drupal::pathValidator()->getUrlIfValid($value['url']);
if (!empty($value['uri'])) {
$url = \Drupal::pathValidator()->getUrlIfValid($value['uri']);
if (!$url) {
return $values;
}
// @todo Don't use the toArray method here. Removed once it is
// deprecated.
$value += $url->toArray();
$value += ['options' => []];
// Reset the URL value to contain only the path.
if (!$url->isExternal() && $this->supportsInternalLinks()) {
$value['url'] = substr($url->toString(), strlen(\Drupal::request()->getBasePath() . '/'));
$value['uri'] = substr($url->toString(), strlen(\Drupal::request()->getBasePath() . '/'));
}
}
}
......
......@@ -52,7 +52,7 @@ public function validate($value, Constraint $constraint) {
/** @var $link_item \Drupal\link\LinkItemInterface */
$link_item = $value;
$link_type = $link_item->getFieldDefinition()->getSetting('link_type');
$url_string = $link_item->url;
$url_string = $link_item->uri;
// Validate the url property.
if ($url_string !== '') {
if ($url = \Drupal::pathValidator()->getUrlIfValid($url_string)) {
......
......@@ -88,7 +88,7 @@ function testURLValidation() {
// Display creation form.
$this->drupalGet('entity_test/add');
$this->assertFieldByName("{$field_name}[0][url]", '', 'Link URL field is displayed');
$this->assertFieldByName("{$field_name}[0][uri]", '', 'Link URL field is displayed');
$this->assertRaw('placeholder="http://example.com"');
// Create a path alias.
......@@ -143,7 +143,7 @@ function testURLValidation() {
protected function assertValidEntries($field_name, array $valid_entries) {
foreach ($valid_entries as $value) {
$edit = array(
"{$field_name}[0][url]" => $value,
"{$field_name}[0][uri]" => $value,
);
$this->drupalPostForm('entity_test/add', $edit, t('Save'));
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
......@@ -164,7 +164,7 @@ protected function assertValidEntries($field_name, array $valid_entries) {
protected function assertInvalidEntries($field_name, array $invalid_entries) {
foreach ($invalid_entries as $invalid_value) {
$edit = array(
"{$field_name}[0][url]" => $invalid_value,
"{$field_name}[0][uri]" => $invalid_value,
);
$this->drupalPostForm('entity_test/add', $edit, t('Save'));
$this->assertText(t('The URL @url is not valid.', array('@url' => $invalid_value)));
......@@ -219,7 +219,7 @@ function testLinkTitle() {
$this->drupalGet('entity_test/add');
// Assert label is shown.
$this->assertText('Read more about this entity');
$this->assertFieldByName("{$field_name}[0][url]", '', 'URL field found.');
$this->assertFieldByName("{$field_name}[0][uri]", '', 'URL field found.');
$this->assertRaw('placeholder="http://example.com"');
if ($title_setting === DRUPAL_DISABLED) {
......@@ -233,14 +233,14 @@ function testLinkTitle() {
if ($title_setting === DRUPAL_REQUIRED) {
// Verify that the link text is required, if the URL is non-empty.
$edit = array(
"{$field_name}[0][url]" => 'http://www.example.com',
"{$field_name}[0][uri]" => 'http://www.example.com',
);
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertText(t('!name field is required.', array('!name' => t('Link text'))));
// Verify that the link text is not required, if the URL is empty.
$edit = array(
"{$field_name}[0][url]" => '',
"{$field_name}[0][uri]" => '',
);
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertNoText(t('!name field is required.', array('!name' => t('Link text'))));
......@@ -248,7 +248,7 @@ function testLinkTitle() {
// Verify that a URL and link text meets requirements.
$this->drupalGet('entity_test/add');
$edit = array(
"{$field_name}[0][url]" => 'http://www.example.com',
"{$field_name}[0][uri]" => 'http://www.example.com',
"{$field_name}[0][title]" => 'Example',
);
$this->drupalPostForm(NULL, $edit, t('Save'));
......@@ -260,7 +260,7 @@ function testLinkTitle() {
// Verify that a link without link text is rendered using the URL as text.
$value = 'http://www.example.com/';
$edit = array(
"{$field_name}[0][url]" => $value,
"{$field_name}[0][uri]" => $value,
"{$field_name}[0][title]" => '',
);
$this->drupalPostForm(NULL, $edit, t('Save'));
......@@ -332,10 +332,10 @@ function testLinkFormatter() {
// Intentionally contains an ampersand that needs sanitization on output.
$title2 = 'A very long & strange example title that could break the nice layout of the site';
$edit = array(
"{$field_name}[0][url]" => $url1,
"{$field_name}[0][uri]" => $url1,
// Note that $title1 is not submitted.
"{$field_name}[0][title]" => '',
"{$field_name}[1][url]" => $url2,
"{$field_name}[1][uri]" => $url2,
"{$field_name}[1][title]" => $title2,
);
// Assert label is shown.
......@@ -470,8 +470,8 @@ function testLinkSeparateFormatter() {
// Intentionally contains an ampersand that needs sanitization on output.
$title2 = 'A very long & strange example title that could break the nice layout of the site';
$edit = array(
"{$field_name}[0][url]" => $url1,
"{$field_name}[1][url]" => $url2,
"{$field_name}[0][uri]" => $url1,
"{$field_name}[1][uri]" => $url2,
"{$field_name}[1][title]" => $title2,
);
$this->drupalPostForm(NULL, $edit, t('Save'));
......
......@@ -28,6 +28,7 @@ class LinkItemTest extends FieldUnitTestBase {
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['router']);
// Create a link field for validation.
entity_create('field_storage_config', array(
......@@ -52,7 +53,7 @@ public function testLinkItem() {
$parsed_url = UrlHelper::parse($url);
$title = $this->randomMachineName();
$class = $this->randomMachineName();
$entity->field_test->url = $parsed_url['path'];
$entity->field_test->uri = $parsed_url['path'];
$entity->field_test->title = $title;
$entity->field_test->first()->get('options')->set('query', $parsed_url['query']);
$entity->field_test->first()->get('options')->set('attributes', array('class' => $class));
......@@ -64,8 +65,8 @@ public function testLinkItem() {
$entity = entity_load('entity_test', $id);
$this->assertTrue($entity->field_test instanceof FieldItemListInterface, 'Field implements interface.');
$this->assertTrue($entity->field_test[0] instanceof FieldItemInterface, 'Field item implements interface.');
$this->assertEqual($entity->field_test->url, $parsed_url['path']);
$this->assertEqual($entity->field_test[0]->url, $parsed_url['path']);
$this->assertEqual($entity->field_test->uri, $parsed_url['path']);
$this->assertEqual($entity->field_test[0]->uri, $parsed_url['path']);
$this->assertEqual($entity->field_test->title, $title);
$this->assertEqual($entity->field_test[0]->title, $title);
$this->assertEqual($entity->field_test->options['attributes']['class'], $class);
......@@ -77,7 +78,7 @@ public function testLinkItem() {
$entity->save();
$id = $entity->id();
$entity = entity_load('entity_test', $id);
$this->assertEqual($entity->field_test->url, $parsed_url['path']);
$this->assertEqual($entity->field_test->uri, $parsed_url['path']);
$this->assertEqual($entity->field_test->options['attributes']['class'], $class);
$this->assertEqual($entity->field_test->options['query'], $parsed_url['query']);
......@@ -85,11 +86,11 @@ public function testLinkItem() {
$new_url = 'http://drupal.org';
$new_title = $this->randomMachineName();
$new_class = $this->randomMachineName();
$entity->field_test->url = $new_url;
$entity->field_test->uri = $new_url;
$entity->field_test->title = $new_title;
$entity->field_test->first()->get('options')->set('query', NULL);
$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->uri, $new_url);
$this->assertEqual($entity->field_test->title, $new_title);
$this->assertEqual($entity->field_test->options['attributes']['class'], $new_class);
$this->assertNull($entity->field_test->options['query']);
......@@ -97,7 +98,7 @@ public function testLinkItem() {
// 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->uri, $new_url);
$this->assertEqual($entity->field_test->title, $new_title);
$this->assertEqual($entity->field_test->options['attributes']['class'], $new_class);
......
......@@ -8,16 +8,39 @@
namespace Drupal\migrate_drupal\Plugin\migrate\process\d6;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\Entity\MigrationInterface;
use Drupal\migrate\MigrateExecutable;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
use Drupal\migrate\Plugin\migrate\process\Route;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* @MigrateProcessPlugin(
* id = "d6_cck_link"
* )
*/
class CckLink extends Route implements ContainerFactoryPluginInterface {
class CckLink extends ProcessPluginBase implements ContainerFactoryPluginInterface {
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->migration = $migration;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$migration
);
}
/**
* {@inheritdoc}
......@@ -27,10 +50,9 @@ public function transform($value, MigrateExecutable $migrate_executable, Row $ro
// Drupal 6 link attributes are double serialized.
$attributes = unserialize(unserialize($attributes));
$route_plugin_value = [$url, []];
$route = parent::transform($route_plugin_value, $migrate_executable, $row, $destination_property);
// Massage the values into the correct form for the link.
$route['uri'] = $url;
$route['options']['attributes'] = $attributes;
$route['title'] = $title;
return $route;
......
......@@ -179,9 +179,8 @@ public function testCckFields() {
$this->assertEqual($node->field_test_exclude_unset->value, 'This is a field with exclude unset.', 'Field with exclude unset is correct.');
// Test that link fields are migrated.
$this->assertIdentical($node->field_test_link->url, 'http://drupal.org/project/drupal');
$this->assertIdentical($node->field_test_link->uri, 'http://drupal.org/project/drupal');
$this->assertIdentical($node->field_test_link->title, 'Drupal project page');
$this->assertIdentical($node->field_test_link->route_parameters, []);
$this->assertIdentical($node->field_test_link->options['attributes'], ['target' => '_blank']);
// Test the file field meta.
......
......@@ -6,6 +6,7 @@
namespace Drupal\rdf\Tests\Field;
use Drupal\Component\Utility\Unicode;
use Drupal\rdf\Tests\Field\FieldRdfaTestBase;
/**
......@@ -48,7 +49,7 @@ public function testAllFormattersExternal() {
// Set up test values.
$this->testValue = 'http://test.me/foo/bar/neque/porro/quisquam/est/qui-dolorem?foo/bar/neque/porro/quisquam/est/qui-dolorem';
$this->entity = entity_create('entity_test', array());
$this->entity->{$this->fieldName}->url = $this->testValue;
$this->entity->{$this->fieldName}->uri = $this->testValue;
// Set up the expected result.
$expected_rdf = array(
......@@ -66,8 +67,7 @@ public function testAllFormattersInternal() {
// Set up test values.
$this->testValue = 'admin';
$this->entity = entity_create('entity_test', array());
$this->entity->{$this->fieldName}->route_name = 'system.admin';
$this->entity->{$this->fieldName}->url = 'admin';
$this->entity->{$this->fieldName}->uri = 'admin';
// Set up the expected result.
// AssertFormatterRdfa looks for a full path.
......@@ -86,8 +86,7 @@ public function testAllFormattersFront() {
// Set up test values.
$this->testValue = '<front>';
$this->entity = entity_create('entity_test', array());
$this->entity->{$this->fieldName}->route_name = $this->testValue;
$this->entity->{$this->fieldName}->url = '<front>';
$this->entity->{$this->fieldName}->uri = '<front>';
// Set up the expected result.
$expected_rdf = array(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment