9

Is there any way to allow the special character in username like "#" and "~"?

11

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.

|improve this answer|||||
  • 5
    Note, $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. – David Thomas May 21 '13 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. ;) – Sumit Madan May 21 '13 at 8:54
  • Also worth noting that this method is far preferable to hacking the Drupal core function inside user.module – sheldonkreger Mar 3 '15 at 23:51
  • It's also possible to use function MODULE_form_user_register_form_alter() – sheldonkreger Mar 4 '15 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. – donut Dec 14 '15 at 20:35
1

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.

|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.