Is there any way to allow the special character in username like "#" and "~"?
2 Answers
You have to override the defualt user_validate_name($name) :
Verify the syntax of the given name.
To do that, define your username validation code in your custom module
function MODULENAME_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'user_register') {
$form['#validate'] = array();
$form['#validate'][] = 'registration_username_validate';
}
}
And inside registration_username_validate function do your logic to allow your special characters.
Care about security issues caused by allowing special chars in username and about display issues not sanitized usernames may cause in themes.
-
5Note,
$form['#validate'] = array();
will clobber any existing validate handlers, including potentially those set by other contrib/custom modules. Better to selectively unset overridden validate function. May 21, 2013 at 8:28 -
@rock thanx. I checked the old database i have only two users so I will notify user of change their username. Thanks btw your solution is good. ;) May 21, 2013 at 8:54
-
Also worth noting that this method is far preferable to hacking the Drupal core function inside user.module Mar 3, 2015 at 23:51
-
It's also possible to use function MODULE_form_user_register_form_alter() Mar 4, 2015 at 20:51
-
This answer is dangerous. As David Thomas mentioned this will replace any other validators, including the default
user_account_form_validate
function which validates more than just the username. Replacing this could allow duplicate usernames! I've added an answer that gets around these issues.– donutDec 14, 2015 at 20:35
Unfortunately, there is no straight forward way to do this. By default user_register_form
and user_profile_form
have user_account_form_validate
set as their first validator in $form['#validate']
. user_account_form_validate()
checks and cleans up the name, email, and signature of an account. As part of checking the name it makes a call to user_validate_name()
. This is the function we want to override, not user_account_form_validate
.
One would hope for a helpful hook to override it, but alas. If I didn't care
about validation of the email and signature as well the check to see if the
name is a duplicate, I could just remove user_account_form_validate
from
$form['#validate']
. But that's no good. Instead, I add an additional
validator that undoes the work of user_validate_name()
and redoes everything
without the special character check.
<?php
function MODULENAME_form_user_register_form_alter(
array &$form, array &$form_state, $form_id)
{
MODULENAME_add_name_validator($form);
}
function MODULENAME_form_user_profile_form_alter(
array &$form, array &$form_state, $form_id)
{
MODULENAME_add_name_validator($form);
}
function MODULENAME_add_name_validator(array &$form)
{
$validate =& $form['#validate'];
# Since `validate_name()` clears out any errors for the "name" field, we
# want to put it right after the validator we want to partially override.
$acct_validate_index = array_search('user_account_form_validate', $validate);
array_splice($validate, ($acct_validate_index + 1), 0,
['MODULENAME_validate_name']
);
}
function MODULENAME_validate_name(array $form, array &$form_state)
{
# There is no blessed way of overriding the call to `user_validate_name()` in
# `user_account_form_validate()`.
$errors =& drupal_static('form_set_error', []);
# Yes, this gets the errors. `form_get_error()` uses this method so... yeah.
if (!isset($errors['name']))
# `user_validate_name()` is a superset of what is checked here. If the name
# passed that validation, no need to rerun things.
return;
# `form_set_error()` also calls `drupal_set_message()` if it finds an
# error.
$msg_index = array_search($errors['name'], $_SESSION['messages']['error']);
if ($msg_index !== false) {
unset($_SESSION['messages']['error'][$msg_index]);
if (empty($_SESSION['messages']['error']))
unset($_SESSION['messages']['error']);
}
unset($errors['name']);
$name = isset($form_state['values']['name'])
? $form_state['values']['name'] : null;
# These checks are taken from `user_validate_name()`, simply excluding the
# for characters we don't mind being in the names.
if (!$name)
$error = t('You must enter a username.');
else if (substr($name, 0, 1) == ' ')
$error = t('The username cannot begin with a space.');
else if (substr($name, -1) == ' ')
$error = t('The username cannot end with a space.');
else if (strpos($name, ' ') !== FALSE)
$error = t('The username cannot contain multiple spaces in a row.');
else if (preg_match('/[\x{80}-\x{A0}' . // Non-printable ISO-8859-1 + NBSP
'\x{AD}' . // Soft-hyphen
'\x{2000}-\x{200F}' . // Various space characters
'\x{2028}-\x{202F}' . // Bidirectional text overrides
'\x{205F}-\x{206F}' . // Various text hinting characters
'\x{FEFF}' . // Byte order mark
'\x{FF01}-\x{FF60}' . // Full-width latin
'\x{FFF9}-\x{FFFD}' . // Replacement characters
'\x{0}-\x{1F}]/u', // NULL byte and control characters
$name))
$error = t('The username contains an illegal character.');
else if (drupal_strlen($name) > USERNAME_MAX_LENGTH)
$error = t('The username %name is too long: it must be %max characters '
.'or less.'
,['%name' => $name, '%max' => USERNAME_MAX_LENGTH]);
if (isset($error))
form_set_error('name', $error);
}
There is still a special character check, however it only checks for invisible or special use characters.