diff --git a/core/misc/autocomplete.js b/core/misc/autocomplete.js index 2f4e5f69935..122f05d7e22 100644 --- a/core/misc/autocomplete.js +++ b/core/misc/autocomplete.js @@ -293,10 +293,11 @@ Drupal.ACDB.prototype.search = function (searchString) { this.timer = setTimeout(function () { db.owner.setStatus('begin'); - // Ajax GET request for autocompletion. + // Ajax GET request for autocompletion. We use Drupal.encodePath instead of + // encodeURIComponent to allow autocomplete search terms to contain slashes. $.ajax({ type: 'GET', - url: db.uri + '/' + encodeURIComponent(searchString), + url: db.uri + '/' + Drupal.encodePath(searchString), dataType: 'json', success: function (matches) { if (typeof matches.status === 'undefined' || matches.status !== 0) { diff --git a/core/modules/taxonomy/taxonomy.pages.inc b/core/modules/taxonomy/taxonomy.pages.inc index 4bdcbc5141f..6994b6edfce 100644 --- a/core/modules/taxonomy/taxonomy.pages.inc +++ b/core/modules/taxonomy/taxonomy.pages.inc @@ -106,6 +106,13 @@ function taxonomy_term_feed($term) { * @see taxonomy_field_widget_info() */ function taxonomy_autocomplete($field_name, $tags_typed = '') { + // If the request has a '/' in the search text, then the menu system will have + // split it into multiple arguments, recover the intended $tags_typed. + $args = func_get_args(); + // Shift off the $field_name argument. + array_shift($args); + $tags_typed = implode('/', $args); + // Make sure the field exists and is a taxonomy field. if (!($field = field_info_field($field_name)) || $field['type'] !== 'taxonomy_term_reference') { // Error string. The JavaScript handler will realize this is not JSON and diff --git a/core/modules/taxonomy/taxonomy.test b/core/modules/taxonomy/taxonomy.test index f8f7ed867f1..de50f2a9448 100644 --- a/core/modules/taxonomy/taxonomy.test +++ b/core/modules/taxonomy/taxonomy.test @@ -731,6 +731,58 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { $this->assertRaw($message, t('Autocomplete returns correct error message when the taxonomy field does not exist.')); } + /** + * Tests term autocompletion edge cases with slashes in the names. + */ + function testTermAutocompletion() { + // Add a term with a slash in the name. + $first_term = $this->createTerm($this->vocabulary); + $first_term->name = '10/16/2011'; + taxonomy_term_save($first_term); + // Add another term that differs after the slash character. + $second_term = $this->createTerm($this->vocabulary); + $second_term->name = '10/17/2011'; + taxonomy_term_save($second_term); + // Add another term that has both a comma and a slash character. + $third_term = $this->createTerm($this->vocabulary); + $third_term->name = 'term with, a comma and / a slash'; + taxonomy_term_save($third_term); + + // Try to autocomplete a term name that matches both terms. + // We should get both term in a json encoded string. + $input = '10/'; + $path = 'taxonomy/autocomplete/taxonomy_'; + $path .= $this->vocabulary->machine_name . '/' . $input; + // The result order is not guaranteed, so check each term separately. + $url = url($path, array('absolute' => TRUE)); + $result = drupal_http_request($url); + $data = drupal_json_decode($result->data); + $this->assertEqual($data[$first_term->name], check_plain($first_term->name), 'Autocomplete returned the first matching term'); + $this->assertEqual($data[$second_term->name], check_plain($second_term->name), 'Autocomplete returned the second matching term'); + + // Try to autocomplete a term name that matches first term. + // We should only get the first term in a json encoded string. + $input = '10/16'; + $url = 'taxonomy/autocomplete/taxonomy_'; + $url .= $this->vocabulary->machine_name . '/' . $input; + $this->drupalGet($url); + $target = array($first_term->name => check_plain($first_term->name)); + $this->assertRaw(drupal_json_encode($target), 'Autocomplete returns only the expected matching term.'); + + // Try to autocomplete a term name with both a comma and a slash. + $input = '"term with, comma and / a'; + $url = 'taxonomy/autocomplete/taxonomy_'; + $url .= $this->vocabulary->machine_name . '/' . $input; + $this->drupalGet($url); + $n = $third_term->name; + // Term names containing commas or quotes must be wrapped in quotes. + if (strpos($third_term->name, ',') !== FALSE || strpos($third_term->name, '"') !== FALSE) { + $n = '"' . str_replace('"', '""', $third_term->name) . '"'; + } + $target = array($n => check_plain($third_term->name)); + $this->assertRaw(drupal_json_encode($target), 'Autocomplete returns a term containing a comma and a slash.'); + } + /** * Save, edit and delete a term using the user interface. */