Drupal: Multiple Autocomplete Forms

No comments
Tags:

I've been working on a module for BabelUp lately called user_deco. It allows users to add images ("decos") to the site, and then "buy" them using a point system (userpoints). The decos are then added to the user's profile. It's a great way to encourage community participation, because decos are a fun way to "spend" userpoints (known as BabelPoints on BabelUp) and userpoints are gained for contributing content on the site.

I got hung up as I was developing a way to send decos to other users, however. I wanted a form that would automatically look for users as the current user was typing, known as autocompletion. That's built into Drupal core (Drupal is a system on which the site was built) but only allows one user at a time to be entered into the field. I needed users to be able to send decos to multiple users at a time, separated by commas.

I knew that the privatemsg module did this, so I went and looked at the code there. It needed a lot of tweaking, but this is what I came up with. It works on any generic Drupal form (Drupal 5 and Drupal 6 versions are below).

/**
 * Implementation of hook_menu().
 */
function user_deco_menu($may_cache) {
  $items = array();
  if (!$may_cache) {
    $items[] = array(
      'path' => 'user_deco/autocomplete',
      'title' => t('User Deco user autocomplete'),
      'callback' => 'user_deco_user_autocomplete',
      'access' => user_access('send user_decos'),
      'type' => MENU_CALLBACK,
    );
    //Put another page here where you want the form to show up.
  }
  return $items;
}

/**
 * Form to send decos to other users.
 */
function user_deco_send() {
  $form['name'] = array(
    '#type' => 'textfield',
    '#title' => t('Send to'),
    '#maxlength' => 60,
    '#autocomplete_path' => 'user_deco/autocomplete',
  );
  $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
  return $form;
}

//Put functions here to do something with the result of user_deco_send.

/**
 * Drupal 5 version.  Adapted from the Privatemsg module.
 * Some awkwardness exists if users have commas in their names.
 *
 * @param $string
 *   The list of names.
 */
function user_deco_user_autocomplete($string) {
  $names = explode(',', $string);
  for ($i = 0; $i < count($names); $i++) {
    $names[$i] = trim($names[$i]);
  }
  $search = array_pop($names);

  if ($search != '') {
    $result = db_query_range("SELECT name FROM {users} WHERE status <> 0 AND LOWER(name) LIKE LOWER('%s%%') ORDER BY name ASC", $search, 0, 10);
    $prefix = '';
    if (count($names)) {
      $prefix = implode(', ', $names) .', ';
    }
    $matches = array();
    while ($user = db_fetch_object($result)) {
      $matches[$prefix . $user->name] = check_plain($user->name);
    }
    print drupal_to_js($matches);
    exit();
  }
}

/**
 * Drupal 6 version.  Adapted from taxonomy.module.
 * Some awkwardness exists if a user has quotes or commas in their username.
 *
 * @param $string
 *   The list of names.
 */
function user_deco_user_autocomplete($string = '') {
  $array = drupal_explode_tags($string);
  //The user enters a comma-separated list of names. We only autocomplete the last name.
  $search = trim(array_pop($array));
  $matches = array();
  if ($search != '') {
    $result = db_query_range("SELECT DISTINCT(name) FROM (SELECT u.name FROM {users} u LEFT JOIN {users_roles} r ON u.uid = r.uid
      WHERE u.status <> 0 AND u.uid <> 0 AND LOWER(u.name) LIKE LOWER('%s%%')
        AND (r.rid IN (SELECT rid FROM {permission} WHERE perm LIKE '%%buy user_decos%%') OR (SELECT rid FROM {permission} WHERE rid = 2 AND perm LIKE '%%buy user_decos%%'))
      ORDER BY u.name ASC) x", $search, 0, 10);
    $prefix = count($array) ? implode(', ', $array) .', ' : '';
    while ($user = db_fetch_object($result)) {
      $name = $user->name;
      //Commas and quotes in terms are special cases, so encode them.  Use strpos() to check if they exist first because str_replace() is expensive.
      if (strpos($user->name, ',') !== FALSE || strpos($user->name, '"') !== FALSE) {
        $name = '"'. str_replace('"', '""', $user->name) .'"';
      }
      $matches[$prefix . $name] = check_plain($user->name);
    }
  }
  drupal_json($matches);
}

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <ul> <ol> <li> <dl> <dt> <dd> <b> <i> <blockquote> <h1> <h2> <h3> <h4> <del> <sub> <sup> <br> <p> <q> <hr> <pre>
  • You may quote other posts using [quote] tags.
  • You can use BBCode tags in the text.
  • Lines and paragraphs break automatically.
  • Textual smileys will be replaced with graphical ones.

More information about formatting options

CAPTCHA
This question helps prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image. Ignore spaces and be careful about upper and lower case.