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