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..df298b1 100644 --- a/link.module +++ b/link.module @@ -465,16 +465,27 @@ function _link_validate(&$item, $delta, $field, $entity, $instance, $langcode, & /** * Clean up user-entered values for a link field according to field settings. * - * @param array $item + * @param array $item * A single link item, usually containing url, title, and attributes. - * @param int $delta + * @param int $delta * The delta value if this field is one of multiple fields. - * @param array $field + * @param array $field * The CCK field definition. - * @param object $entity + * @param object $entity * 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. @@ -1261,7 +1272,7 @@ function link_validate_url($text, $langcode = NULL) { * * @param string $text * Url to be checked. - * + * * @return mixed * Returns boolean FALSE if the URL is not valid. On success, returns one of * the LINK_(linktype) constants. @@ -1439,7 +1450,7 @@ function link_field_settings_form() { /** * Additional callback to adapt the property info of link fields. - * + * * @see entity_metadata_field_entity_property_info() */ function link_field_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) { 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 . "')"); + } + } +}