diff --git a/link.info b/link.info index 2b65825..4659140 100644 --- a/link.info +++ b/link.info @@ -14,6 +14,7 @@ files[] = tests/link.crud_browser.test files[] = tests/link.token.test files[] = tests/link.entity_token.test files[] = tests/link.validate.test +files[] = tests/link.multilingual.test ; Views Handlers files[] = views/link_views_handler_argument_target.inc diff --git a/link.module b/link.module index 1e9d088..25bef6d 100644 --- a/link.module +++ b/link.module @@ -475,6 +475,17 @@ function _link_validate(&$item, $delta, $field, $entity, $instance, $langcode, & * The entity containing this link. */ function _link_sanitize(&$item, $delta, &$field, $instance, &$entity) { + // As this function can be called multiple times and the item is changed by + // reference we need to ensure that there's always the original data to + // process otherwise processed data are processed again which might leads to + // unexpected results. + if (isset($item['_link_sanitized'])) { + return; + } + + // Store a flag to check in case of a second call. + $item['_link_sanitized'] = TRUE; + // Don't try to process empty links. if (empty($item['url']) && empty($item['title'])) { return; @@ -515,13 +526,13 @@ function _link_sanitize(&$item, $delta, &$field, $instance, &$entity) { $url_parts = _link_parse_url($url); if (!empty($url_parts['url'])) { - $item['url'] = url($url_parts['url'], - array('query' => isset($url_parts['query']) ? $url_parts['query'] : NULL, + $item = array( + 'url' => $url_parts['url'], + 'query' => isset($url_parts['query']) ? $url_parts['query'] : NULL, 'fragment' => isset($url_parts['fragment']) ? $url_parts['fragment'] : NULL, 'absolute' => !empty($instance['settings']['absolute_url']), 'html' => TRUE, - ) - ); + ) + $item; } // Create a shortened URL for display. @@ -1483,6 +1494,7 @@ function link_field_item_property_info() { 'type' => 'uri', 'label' => t('The URL of the link.'), 'setter callback' => 'entity_property_verbatim_set', + 'getter callback' => 'link_url_property_get', ); $properties['attributes'] = array( 'type' => 'struct', @@ -1501,6 +1513,13 @@ function link_attribute_property_get($data, array $options, $name, $type, $info) } /** + * Entity property info getter callback for link url. + */ +function link_url_property_get($data, array $options, $name, $type, $info) { + return (isset($data['url'])) ? url($data['url'], $data) : ''; +} + +/** * Implements hook_field_update_instance(). */ function link_field_update_instance($instance, $prior_instance) { diff --git a/tests/link.multilingual.test b/tests/link.multilingual.test new file mode 100644 index 0000000..fd7f648 --- /dev/null +++ b/tests/link.multilingual.test @@ -0,0 +1,191 @@ +permissions = array_merge($this->permissions, array( + 'administer site configuration', + 'administer languages', + )); + parent::setUp($modules); + } + + /** + * Enables and configured language related stuff. + */ + public function setUpLanguage() { + global $language_url; + $this->drupalGet('admin/config/regional/language'); + // Enable the path prefix for the default language: this way any un-prefixed + // URL must have a valid fallback value. + $edit = array('prefix' => 'en'); + $this->drupalPost('admin/config/regional/language/edit/en', $edit, t('Save language')); + $language_url->prefix = $language_url->language; + + // Add custom language - as we need more than 1 language to be multilingual. + // Code for the language. + $langcode = 'xx'; + // The English name for the language. + $name = $this->randomName(16); + // The native name for the language. + $native = $this->randomName(16); + $edit = array( + 'langcode' => $langcode, + 'name' => $name, + 'native' => $native, + 'prefix' => $langcode, + 'direction' => '0', + ); + $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); + variable_set('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX); + + // Enable URL language detection and selection. + $edit = array('language[enabled][locale-url]' => 1); + $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); + language_negotiation_set(LANGUAGE_TYPE_INTERFACE, array(LOCALE_LANGUAGE_NEGOTIATION_URL)); + + // Reset static caching. + drupal_static_reset('language_list'); + drupal_static_reset('locale_url_outbound_alter'); + drupal_static_reset('locale_language_url_rewrite_url'); + } +} + +class LinkMultilingualPathTest extends LinkMultilingualTestCase { + + public static function getInfo() { + return array( + 'name' => 'Link language path prefix', + 'description' => 'Tests that path properly work with language path prefixes.', + 'group' => 'Link', + ); + } + + /** + * Creates a link field, fills it, then uses a loaded node to test paths. + */ + public function testLanguagePrefixedPaths() { + $this->setUpLanguage(); + + // Create fields. + // Field for absolute urls. + $field_name_absolute = $this->createLinkField('page'); + + // Field for relative urls. + $settings = array( + 'instance[settings][absolute_url]' => FALSE, + ); + $field_name_relative = $this->createLinkField('page', $settings); + + // Check the node edit form. + $this->drupalGet('node/add/page'); + $this->assertField($field_name_absolute . '[und][0][title]', 'Title absolute found'); + $this->assertField($field_name_absolute . '[und][0][url]', 'URL absolute found'); + $this->assertField($field_name_relative . '[und][0][title]', 'Title relative found'); + $this->assertField($field_name_relative . '[und][0][url]', 'URL relative found'); + + // Create test content. + $url_tests = array( + 1 => array( + 'href' => 'http://dummy.com/' . $this->randomName(), + 'label' => $this->randomName(), + ), + 2 => array( + 'href' => 'node/1', + 'label' => $this->randomName(), + ), + 3 => array( + 'href' => 'node/1?property=value', + 'label' => $this->randomName(), + 'query' => array('property' => 'value'), + ), + 4 => array( + 'href' => 'node/1#position', + 'label' => $this->randomName(), + 'fragment' => 'position', + ), + 5 => array( + 'href' => 'node/1?property=value2#lower', + 'label' => $this->randomName(), + 'fragment' => 'lower', + 'query' => array('property' => 'value2'), + ), + ); + foreach ($url_tests as $index => &$input) { + $this->drupalGet('node/add/page'); + + $edit = array( + 'title' => $input['label'], + $field_name_absolute . '[und][0][title]' => $input['label'], + $field_name_absolute . '[und][0][url]' => $input['href'], + $field_name_relative . '[und][0][title]' => $input['label'], + $field_name_relative . '[und][0][url]' => $input['href'], + ); + $this->drupalPost(NULL, $edit, t('Save')); + $url = $this->getUrl(); + $input['url'] = $url; + } + + // Change to anonymous user. + $this->drupalLogout(); + + foreach (array_slice($url_tests, 1, NULL, TRUE) as $index => $input2) { + $node = node_load($index); + $this->assertNotEqual(NULL, $node, "Do we have a node?"); + $this->assertEqual($node->nid, $index, "Test that we have a node."); + $this->drupalGet('node/' . $index); + + $relative_expected = url('node/1', array('absolute' => FALSE) + $input2); + $absolute_expected = url('node/1', array('absolute' => TRUE) + $input2); + + $absolute_result = $this->xpath('//*[contains(@class, "field-name-' . drupal_clean_css_identifier($field_name_absolute) . '")]/div/div/a/@href'); + $absolute_result = (string) reset($absolute_result); + $this->assertEqual($absolute_result, $absolute_expected, "Absolute url output ('" . $absolute_result . "') looks as expected ('" . $absolute_expected . "')"); + + $relative_result = $this->xpath('//*[contains(@class, "field-name-' . drupal_clean_css_identifier($field_name_relative) . '")]/div/div/a/@href'); + $relative_result = (string) reset($relative_result); + $this->assertEqual($relative_result, $relative_expected, "Relative url output ('" . $relative_result . "') looks as expected ('" . $relative_expected . "')"); + } + + // Check if this works with the alias too. + // Add a path alias for node 1. + $path = array( + 'source' => 'node/1', + 'alias' => $url_tests[1]['label'], + ); + path_save($path); + // Another iteration over the same nodes - this time they should use the + // path alias. + foreach (array_slice($url_tests, 1, NULL, TRUE) as $index => $input2) { + $node = node_load($index); + $this->assertNotEqual(NULL, $node, "Do we have a node?"); + $this->assertEqual($node->nid, $index, "Test that we have a node."); + $this->drupalGet('node/' . $index); + + $relative_expected = url('node/1', array('absolute' => FALSE) + $input2); + $absolute_expected = url('node/1', array('absolute' => TRUE) + $input2); + + $absolute_result = $this->xpath('//*[contains(@class, "field-name-' . drupal_clean_css_identifier($field_name_absolute) . '")]/div/div/a/@href'); + $absolute_result = (string) reset($absolute_result); + $this->assertEqual($absolute_result, $absolute_expected, "Absolute alias-url output ('" . $absolute_result . "') looks as expected ('" . $absolute_expected . "')"); + + $relative_result = $this->xpath('//*[contains(@class, "field-name-' . drupal_clean_css_identifier($field_name_relative) . '")]/div/div/a/@href'); + $relative_result = (string) reset($relative_result); + $this->assertEqual($relative_result, $relative_expected, "Relative alias-url output ('" . $relative_result . "') looks as expected ('" . $relative_expected . "')"); + } + } +}