Commit f1b6874e authored by Dries's avatar Dries

Issue #1831324 by sun, effulgentsia: Form API #autocomplete_path() JS and...

Issue #1831324 by sun, effulgentsia: Form API #autocomplete_path() JS and callbacks to use GET q= parameter instead of menu tail.
parent ac21d7bc
......@@ -3890,36 +3890,13 @@ function theme_vertical_tabs($variables) {
/**
* Adds autocomplete functionality to elements with a valid #autocomplete_path.
*
* Note that autocomplete callbacks should include special handling as the
* user's input may contain forward slashes. If the user-submitted string has a
* '/' in the text that is sent in the autocomplete request, the menu system
* will split the text and pass it to the callback as multiple arguments.
*
* Suppose your autocomplete path in the menu system is 'mymodule_autocomplete.'
* In your form you have:
* @code
* '#autocomplete_path' => 'mymodule_autocomplete/' . $some_key . '/' . $some_id,
* @endcode
* The user types in "keywords" so the full path called is:
* 'mymodule_autocomplete/$some_key/$some_id/keywords'
*
* You should include code similar to the following to handle slashes in the
* input:
* @code
* function mymodule_autocomplete_callback($arg1, $arg2, $keywords) {
* $args = func_get_args();
* // We need to remove $arg1 and $arg2 from the beginning of the array so we
* // are left with the keywords.
* array_shift($args);
* array_shift($args);
* // We store the user's original input in $keywords, including any slashes.
* // Note: A prepended or trailing slash will be removed. For example, if the
* // user enters '/a/few/words/' then $keywords will contain 'a/few/words'.
* $keywords = implode('/', $args);
*
* // Your code here.
* }
* @endcode
* 'mymodule_autocomplete/$some_key/$some_id?q=keywords'
*
* @param $element
* The form element to process. Properties used:
......
......@@ -297,11 +297,13 @@ Drupal.ACDB.prototype.search = function (searchString) {
this.timer = setTimeout(function () {
db.owner.setStatus('begin');
// Ajax GET request for autocompletion. We use Drupal.encodePath instead of
// encodeURIComponent to allow autocomplete search terms to contain slashes.
// Ajax GET request for autocompletion.
$.ajax({
type: 'GET',
url: db.uri + '/' + Drupal.encodePath(searchString),
url: db.uri,
data: {
q: searchString
},
dataType: 'json',
success: function (matches) {
if (typeof matches.status === 'undefined' || matches.status !== 0) {
......
......@@ -39,7 +39,7 @@ function setUp() {
*/
function testUserAutocomplete() {
$this->drupalLogin($this->account);
$this->drupalGet('user/autocomplete/' . $this->account->name);
$this->drupalGet('user/autocomplete', array('query' => array('q' => $this->account->name)));
$this->assertRaw($this->account->name);
$this->assertNoText('registry initialized', 'The registry was not initialized');
}
......
......@@ -214,13 +214,13 @@ function testNodeTermCreationAndDeletion() {
// Test autocomplete on term 3, which contains a comma.
// The term will be quoted, and the " will be encoded in unicode (\u0022).
$input = substr($term_objects['term3']->name, 0, 3);
$json = $this->drupalGet('taxonomy/autocomplete/taxonomy_' . $this->vocabulary->machine_name . '/' . $input);
$json = $this->drupalGet('taxonomy/autocomplete/taxonomy_' . $this->vocabulary->machine_name, array('query' => array('q' => $input)));
$this->assertEqual($json, '{"\u0022' . $term_objects['term3']->name . '\u0022":"' . $term_objects['term3']->name . '"}', format_string('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term3']->name)));
// Test autocomplete on term 4 - it is alphanumeric only, so no extra
// quoting.
$input = substr($term_objects['term4']->name, 0, 3);
$this->drupalGet('taxonomy/autocomplete/taxonomy_' . $this->vocabulary->machine_name . '/' . $input);
$this->drupalGet('taxonomy/autocomplete/taxonomy_' . $this->vocabulary->machine_name, array('query' => array('q' => $input)));
$this->assertRaw('{"' . $term_objects['term4']->name . '":"' . $term_objects['term4']->name . '"}', format_string('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term4']->name)));
// Test taxonomy autocomplete with a nonexistent field.
......@@ -228,7 +228,7 @@ function testNodeTermCreationAndDeletion() {
$tag = $this->randomName();
$message = t("Taxonomy field @field_name not found.", array('@field_name' => $field_name));
$this->assertFalse(field_info_field($field_name), format_string('Field %field_name does not exist.', array('%field_name' => $field_name)));
$this->drupalGet('taxonomy/autocomplete/' . $field_name . '/' . $tag);
$this->drupalGet('taxonomy/autocomplete/' . $field_name, array('query' => array('q' => $tag)));
$this->assertRaw($message, 'Autocomplete returns correct error message when the taxonomy field does not exist.');
}
......@@ -252,10 +252,9 @@ function testTermAutocompletion() {
// 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;
$path = 'taxonomy/autocomplete/taxonomy_' . $this->vocabulary->machine_name;
// The result order is not guaranteed, so check each term separately.
$result = $this->drupalGet($path);
$result = $this->drupalGet($path, array('query' => array('q' => $input)));
$data = drupal_json_decode($result);
$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');
......@@ -263,17 +262,15 @@ function testTermAutocompletion() {
// 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);
$path = 'taxonomy/autocomplete/taxonomy_' . $this->vocabulary->machine_name;
$this->drupalGet($path, array('query' => array('q' => $input)));
$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);
$path = 'taxonomy/autocomplete/taxonomy_' . $this->vocabulary->machine_name;
$this->drupalGet($path, array('query' => array('q' => $input)));
$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) {
......
......@@ -307,9 +307,10 @@ function taxonomy_menu() {
'type' => MENU_CALLBACK,
'file' => 'taxonomy.pages.inc',
);
$items['taxonomy/autocomplete'] = array(
$items['taxonomy/autocomplete/%'] = array(
'title' => 'Autocomplete taxonomy',
'page callback' => 'taxonomy_autocomplete',
'page arguments' => array(2),
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
'file' => 'taxonomy.pages.inc',
......
......@@ -102,21 +102,14 @@ function taxonomy_term_feed(Term $term) {
*
* @param $field_name
* The name of the term reference field.
* @param $tags_typed
* (optional) A comma-separated list of term names entered in the
* autocomplete form element. Only the last term is used for autocompletion.
* Defaults to '' (an empty string).
*
* @see taxonomy_menu()
* @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);
function taxonomy_autocomplete($field_name) {
// A comma-separated list of term names entered in the autocomplete form
// element. Only the last term is used for autocompletion.
$tags_typed = drupal_container()->get('request')->query->get('q');
// Make sure the field exists and is a taxonomy field.
if (!($field = field_info_field($field_name)) || $field['type'] !== 'taxonomy_term_reference') {
......
......@@ -35,13 +35,13 @@ function setUp() {
function testUserAutocomplete() {
// Check access from unprivileged user, should be denied.
$this->drupalLogin($this->unprivileged_user);
$this->drupalGet('user/autocomplete/' . $this->unprivileged_user->name[0]);
$this->drupalGet('user/autocomplete', array('query' => array('q' => $this->unprivileged_user->name[0])));
$this->assertResponse(403, 'Autocompletion access denied to user without permission.');
// Check access from privileged user.
$this->drupalLogout();
$this->drupalLogin($this->privileged_user);
$this->drupalGet('user/autocomplete/' . $this->unprivileged_user->name[0]);
$this->drupalGet('user/autocomplete', array('query' => array('q' => $this->unprivileged_user->name[0])));
$this->assertResponse(200, 'Autocompletion access allowed.');
// Using first letter of the user's name, make sure the user's full name is in the results.
......
......@@ -14,9 +14,9 @@
/**
* Menu callback; Retrieve a JSON object containing autocomplete suggestions for existing users.
*/
function user_autocomplete($string = '') {
function user_autocomplete() {
$matches = array();
if ($string) {
if ($string = drupal_container()->get('request')->query->get('q')) {
$result = db_select('users')->fields('users', array('name'))->condition('name', db_like($string) . '%', 'LIKE')->range(0, 10)->execute();
foreach ($result as $account) {
$matches[$account->name] = check_plain($account->name);
......
......@@ -283,12 +283,15 @@ function views_ajax_form_wrapper($form_id, &$form_state) {
*
* @param string $string
* (optional) A comma-separated list of user names entered in the
* autocomplete form element. Only the last user is used for autocompletion.
* Defaults to '' (an empty string).
* autocomplete form element. If not passed, it is taken from the 'q' query
* string parameter.
*
* @return Symfony\Component\HttpFoundation\JsonResponse
*/
function views_ajax_autocomplete_user($string = '') {
function views_ajax_autocomplete_user($string = NULL) {
if (!isset($string)) {
$string = drupal_container()->get('request')->query->get('q');
}
// The user enters a comma-separated list of user name. We only autocomplete the last name.
$array = drupal_explode_tags($string);
......@@ -328,14 +331,11 @@ function views_ajax_autocomplete_user($string = '') {
* @param $vid
* The vocabulary id of the tags which should be returned.
*
* @param $tags_typed
* The typed string of the user.
*
* @see taxonomy_autocomplete()
*/
function views_ajax_autocomplete_taxonomy($vid, $tags_typed = '') {
function views_ajax_autocomplete_taxonomy($vid) {
// The user enters a comma-separated list of tags. We only autocomplete the last tag.
$tags_typed = drupal_explode_tags($tags_typed);
$tags_typed = drupal_explode_tags(drupal_container()->get('request')->query->get('q'));
$tag_last = drupal_strtolower(array_pop($tags_typed));
$matches = array();
......
......@@ -417,7 +417,7 @@ function views_menu() {
);
// Define another taxonomy autocomplete because the default one of drupal
// does not support a vid a argument anymore
$items['admin/views/ajax/autocomplete/taxonomy'] = array(
$items['admin/views/ajax/autocomplete/taxonomy/%'] = array(
'page callback' => 'views_ajax_autocomplete_taxonomy',
'theme callback' => 'ajax_base_page_theme',
'access callback' => 'user_access',
......
......@@ -2096,8 +2096,11 @@ function views_ui_edit_display_form_change_theme($form, &$form_state) {
/**
* Page callback for views tag autocomplete
*/
function views_ui_autocomplete_tag($string = '') {
function views_ui_autocomplete_tag($string = NULL) {
$matches = array();
if (!isset($string)) {
$string = drupal_container()->get('request')->query->get('q');
}
// get matches from default views:
$views = views_get_all_views();
foreach ($views as $view) {
......
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