username-1.0.x-dev/modules/username_phone/username_phone.module

modules/username_phone/username_phone.module
<?php

/**
 * @file
 * Exposes global functionality for username phone field on user form.
 */

use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\Email;
use Drupal\Core\Url;
use Drupal\phonenumber\Element\Phone;

/**
 * Variety of username mode options.
 *
 * @return array
 *   An array containing different username mode options.
 */
function username_phone_username_mode() {
  return [
    'phone' => t('Phone number'),
  ];
}

/**
 * Implements hook_entity_type_alter().
 */
function username_phone_entity_type_alter(&$entity_types) {
  /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
  if (isset($entity_types['user'])) {
    // Override the user entity class to use our custom class.
    $entity_types['user']->setClass('Drupal\username_phone\Entity\UsernamePhoneUser');
    // Set the custom form class for user registration.
    $entity_types['user']->setFormClass('register', '\Drupal\username_phone\Form\UsernamePhoneForm');
  }
}

/**
 * Implements hook_entity_base_field_info().
 */
function username_phone_entity_base_field_info(EntityTypeInterface $entity_type) {
  // Add a 'Phone' base field to the user account.
  if ($entity_type->id() === 'user') {
    $entity_type_id = $entity_type->id();
    $fields = [];

    // Define the 'phone' field.
    $fields['phone'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Phone number'))
      ->setDescription(t('The phone number of this user.'))
      ->setDefaultValue('')
      ->setTargetEntityTypeId($entity_type_id)
      // Ensure the phone number is unique.
      ->addConstraint('UsernamePhoneUnique')
      // Make the phone number a required field.
      ->addConstraint('UsernamePhoneRequired')
      // Protect the field from unauthorized changes.
      ->addConstraint('ProtectedUserField');

    return $fields;
  }
}

/**
 * Fetches a user object by phone number.
 *
 * @param string $phone
 *   The account's phone number.
 *
 * @return \Drupal\user\UserInterface|false
 *   A fully-loaded user object upon successful user load or FALSE if the user
 *   cannot be loaded.
 */
function username_phone_user_load_by_phone($phone) {
  // Load users by the 'phone' property.
  $users = \Drupal::entityTypeManager()->getStorage('user')
    ->loadByProperties(['phone' => $phone]);
  // Return the first user found or FALSE if none found.
  return $users ? reset($users) : FALSE;
}

/**
 * Verify the phone number international format of the given phone.
 *
 * @param string $phone
 *   The user's phone number to validate international format.
 *
 * @return bool
 *   TRUE if the phone starts with a plus and digits; FALSE otherwise.
 */
function username_phone_validate_international_format($phone) {
  // Check if the phone number starts with a plus sign followed by digits.
  return preg_match("/^\+\d+$/", $phone) === 1;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Alters the user login form to include phone number as a username option.
 */
function username_phone_form_user_login_form_alter(&$form, FormStateInterface $form_state) {
  // Get username configuration settings.
  $config_username = \Drupal::config('username.settings');
  $selective = $config_username->get('selective.enabled');
  $username = array_filter($config_username->get('username'));

  // Return if phone is not a username.
  if (!isset($username['phone'])) {
    return;
  }

  // Get username phone configuration settings.
  $config = \Drupal::config('username_phone.settings');

  // Store 'phone' field parameters.
  $phone_field = [
    '#title' => t('Phone number'),
    '#type' => 'phone',
    '#phone' => [
      'allow_dropdown' => $config->get('allow_dropdown'),
      'show_flags' => $config->get('show_flags'),
      'separate_dial_code' => $config->get('separate_dial_code'),
      'countries' => $config->get('countries'),
      'initial_country' => $config->get('initial_country'),
      'preferred_countries' => array_keys($config->get('preferred_countries')),
      'exclude_countries' => [],
      'strict_mode' => TRUE,
    ],
    '#attributes' => [
      'autocorrect' => 'off',
      'autocapitalize' => 'off',
      'spellcheck' => 'false',
      'autofocus' => 'autofocus',
    ],
    '#required' => FALSE,
    '#weight' => 0,
    '#states' => [
      'visible' => [
        ':input[name="username"]' => ['value' => 'phone'],
      ],
      'required' => [
        ':input[name="username"]' => ['value' => 'phone'],
      ],
    ],
  ];

  // Hidden fields to store phone details.
  $form['phone_number'] = [
    '#type' => 'hidden',
    '#attributes' => ['class' => 'temporary-phone'],
  ];
  $form['country_code'] = [
    '#type' => 'hidden',
    '#attributes' => ['class' => 'temporary-phone'],
  ];
  $form['country_iso2'] = [
    '#type' => 'hidden',
    '#attributes' => ['class' => 'temporary-phone'],
  ];

  // Store 'mail' field parameters.
  $email_field = [
    '#type' => 'email',
    '#title' => t('Email address'),
    '#weight' => 0,
    '#required' => FALSE,
    '#states' => [
      'visible' => [
        ':input[name="username"]' => ['value' => 'mail'],
      ],
      'required' => [
        ':input[name="username"]' => ['value' => 'mail'],
      ],
    ],
  ];

  // Store 'name' field parameters.
  $name_states = [
    '#required' => FALSE,
    'visible' => [
      ':input[name="username"]' => ['value' => 'name'],
    ],
    'required' => [
      ':input[name="username"]' => ['value' => 'name'],
    ],
  ];

  // Only phone is a username.
  if (count($username) == 1) {
    $form['name'] = array_merge($form['name'], $phone_field);
    $form['name']['#maxlength'] = Phone::PHONE_MAX_LENGTH;
    $form['name']['#required'] = TRUE;
  }

  // Name and phone are usernames at the same time.
  if (count($username) == 2 && isset($username['name'])) {
    if ($selective) {
      $form['phone'] = $phone_field;
      $form['name']['#states'] = $name_states;
    }
  }

  // Mail and phone are usernames at the same time.
  if (count($username) == 2 && isset($username['mail'])) {
    if ($selective) {
      $form['phone'] = $phone_field;
      $form['name'] = array_merge($form['name'], $email_field);
    }
    else {
      $form['name']['#maxlength'] = max(Phone::PHONE_MAX_LENGTH, Email::EMAIL_MAX_LENGTH);
    }
  }

  // Name, mail, and phone are usernames at the same time.
  if (count($username) == 3 && isset($username['name']) && isset($username['mail'])) {
    if ($selective) {
      $form['phone'] = $phone_field;
      $form['mail'] = $email_field;
      $form['name']['#states'] = $name_states;
    }
    else {
      $form['name']['#maxlength'] = max(Phone::PHONE_MAX_LENGTH, Email::EMAIL_MAX_LENGTH);
    }
  }

  // Add phone username element validation.
  $form['name']['#element_validate'][] = 'username_phone_user_login_validate';

  // Set username labels.
  username_set_label($form);

  // Set username placeholders.
  username_set_placeholder($form);

  // Set username descriptions.
  username_set_description($form);
}

/**
 * Form element validation handler for the user login form.
 *
 * Allows users to authenticate by phone number.
 *
 * @param array $form
 *   An associative array containing the structure of the form.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The current state of the form.
 */
function username_phone_user_login_validate(array $form, FormStateInterface $form_state) {
  // Fetch the form values.
  $values = $form_state->getValues();

  // Get the username configuration settings.
  $config = \Drupal::config('username.settings');
  $usernames = array_filter($config->get('username'));

  // Check if phone is enabled as a username.
  if (isset($usernames['phone'])) {
    // Determine which field to validate based on
    // the presence of 'phone' field in form values.
    $field = isset($values['phone']) ? 'phone' : 'name';
    $phone = isset($values['phone']) ? $values['phone_number'] : $values['name'];

    if (!empty($phone)) {
      // Detect the type of the entered username (e.g., email, phone).
      $type = username_detect_input_type($phone);

      // Skip phone validation if input type is an
      // email format and mail is enabled as username.
      if (isset($usernames['mail']) && $type === 'email') {
        return;
      }

      // Validate phone number format if core username is not active.
      if (!isset($usernames['name']) && $type === 'phone' && !username_phone_validate_international_format($phone)) {
        $form_state->setErrorByName(
          $field,
          t('Please make sure to include the "+" sign and your country code.')
        );
        return;
      }

      // Load user by phone number if any match.
      if ($user = username_phone_user_load_by_phone($phone)) {
        // Check if the user account is active and not blocked.
        if (!$user->isActive()) {
          $form_state->setErrorByName(
            $field,
            t('The account with the phone number %name has not been activated or is blocked.', ['%name' => $phone])
          );
        }
        else {
          // Replace the entered name with the user's
          // account name to continue the default login process.
          $form_state->setValue('name', $user->getAccountName());
        }
      }
      elseif (count($usernames) == 1 || isset($values['phone'])) {
        // If no user is found by phone and phone is
        // the only username method, set an error.
        $query = isset($form_state->getUserInput()[$field]) ? ['name' => $form_state->getUserInput()[$field]] : [];
        $form_state->setErrorByName(
          $field,
          t('Unrecognized phone number or password. <a href=":password">Forgot your password?</a>', [
            ':password' => Url::fromRoute('user.pass', [], ['query' => $query])->toString(),
          ])
        );
      }
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function username_phone_form_user_pass_alter(&$form, FormStateInterface $form_state) {
  // Load username configuration settings.
  $config = \Drupal::config('username.settings');
  $usernames = array_filter($config->get('username'));
  $selective = $config->get('selective.enabled');
  $override = $config->get('override');

  // Return if phone is not a username.
  if (!isset($usernames['phone'])) {
    return;
  }

  // Change default name field if only phone is a username.
  if (count($usernames) == 1) {
    $form['name']['#title'] = t('Phone number');
    if ($form['name']['#type'] === 'textfield') {
      $form['name']['#type'] = 'phone';
      $form['name']['#phone']['show_flags'] = TRUE;
      $form['name']['#phone']['allow_dropdown'] = TRUE;
      $form['name']['#phone']['separate_dial_code'] = TRUE;
      $form['name']['#maxlength'] = Phone::PHONE_MAX_LENGTH;
      $form['mail']['#markup'] = t('Password reset instructions will be sent to your registered phone number.');
    }
  }

  // Change default title if override is enabled.
  if (count($usernames) == 2 && isset($usernames['mail'])) {
    if ($form['name']['#type'] === 'textfield') {
      $form['name']['#title'] = !$selective && $override['username'] && !empty($label['username']) ? $label['username'] : t('Email address or phone number');
      $form['name']['#maxlength'] = max(Phone::PHONE_MAX_LENGTH, Email::EMAIL_MAX_LENGTH);
    }
  }

  if (count($usernames) == 3 && isset($usernames['name']) && isset($usernames['mail'])) {
    if ($form['name']['#type'] === 'textfield') {
      $form['name']['#title'] = !$selective && $override['username'] && !empty($label['username']) ? $label['username'] : t('Username, email address or phone number');
      $form['name']['#maxlength'] = max(Phone::PHONE_MAX_LENGTH, Email::EMAIL_MAX_LENGTH);
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function username_phone_form_username_admin_settings_alter(&$form, FormStateInterface $form_state) {
  // Load username phone configuration settings.
  $config = \Drupal::config('username_phone.settings');

  // Get list of countries.
  $countries = \Drupal::service('country_manager')->getList();

  // Phone setting fields.
  $form['phone_settings'] = [
    '#type' => 'details',
    '#title' => t('Phone settings'),
    '#open' => TRUE,
    '#weight' => 3,
  ];
  $form['phone_settings']['initial_country'] = [
    '#type' => 'select',
    '#title' => t('Default country'),
    '#options' => ['auto' => t("Automatically detect user's country (geoIPLookup)")] + $countries,
    '#default_value' => $config->get('initial_country'),
    '#description' => t('Specify a default selection country. If geolocation is enabled, this setting will be ignored.'),
  ];
  $form['phone_settings']['preferred_countries'] = [
    '#type' => 'select',
    '#title' => t('Preferred countries'),
    '#multiple' => TRUE,
    '#options' => $countries,
    '#default_value' => $config->get('preferred_countries'),
    '#description' => t('Specify the countries to appear at the top of the list. Leave it blank to follow an alphabetical order.'),
  ];
  $form['phone_settings']['countries'] = [
    '#type' => 'radios',
    '#title' => t('List of countries'),
    '#options' => [
      'all' => t('All countries'),
      'include' => t('Only the selected countries'),
      'exclude' => t('Exclude the selected countries'),
    ],
    '#default_value' => $config->get('countries'),
  ];
  $form['phone_settings']['exclude_countries'] = [
    '#type' => 'select',
    '#title' => t('Excluded countries'),
    '#title_display' => 'invisible',
    '#options' => $countries,
    '#multiple' => TRUE,
    '#default_value' => $config->get('exclude_countries'),
    '#states' => [
      'invisible' => [
        ':input[name="countries"]' => ['value' => 'all'],
      ],
    ],
  ];
  $form['phone_settings']['allow_dropdown'] = [
    '#type' => 'checkbox',
    '#title' => t('Allow country dropdown'),
    '#default_value' => $config->get('allow_dropdown'),
  ];
  $form['phone_settings']['separate_dial_code'] = [
    '#type' => 'checkbox',
    '#title' => t('Separate dial code'),
    '#default_value' => $config->get('separate_dial_code'),
  ];
  $form['phone_settings']['show_flags'] = [
    '#type' => 'checkbox',
    '#title' => t('Show country flags'),
    '#default_value' => $config->get('show_flags'),
  ];
  $form['phone_settings']['placeholder'] = [
    '#type' => 'textfield',
    '#title' => t('Placeholder text'),
    '#default_value' => $config->get('placeholder'),
    '#description' => t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
  ];

  // Add username phone submit function.
  $form['#submit'][] = 'username_phone_form_username_admin_settings_submit';
}

/**
 * Submit function for username_admin_settings to save our variable.
 *
 * @see username_phone_form_user_admin_settings_alter()
 */
function username_phone_form_username_admin_settings_submit(array &$form, FormStateInterface $form_state) {
  \Drupal::configFactory()->getEditable('username_phone.settings')
    ->set('allow_dropdown', $form_state->getValue('allow_dropdown'))
    ->set('initial_country', $form_state->getValue('initial_country'))
    ->set('preferred_countries', $form_state->getValue('preferred_countries'))
    ->set('separate_dial_code', $form_state->getValue('separate_dial_code'))
    ->set('show_flags', $form_state->getValue('show_flags'))
    ->set('exclude_countries', $form_state->getValue('exclude_countries'))
    ->set('countries', $form_state->getValue('countries'))
    ->save();
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc