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); }