View source
<?php
namespace Drupal\mobile_number\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\user\Entity\User;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\mobile_number\MobileNumberUtilInterface;
use Drupal\Core\Entity\EntityTypeInterface;
class MobileNumberItem extends FieldItemBase {
public static function defaultStorageSettings() {
return [
'unique' => MobileNumberUtilInterface::MOBILE_NUMBER_UNIQUE_NO,
] + parent::defaultStorageSettings();
}
public static function defaultFieldSettings() {
$util = \Drupal::service('mobile_number.util');
return parent::defaultFieldSettings() + [
'verify' => $util
->isSmsEnabled() ? $util::MOBILE_NUMBER_VERIFY_OPTIONAL : MobileNumberUtilInterface::MOBILE_NUMBER_VERIFY_NONE,
'message' => $util::MOBILE_NUMBER_DEFAULT_SMS_MESSAGE,
'countries' => [],
];
}
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return [
'columns' => [
'value' => [
'type' => 'varchar',
'length' => 19,
'not null' => TRUE,
'default' => '',
],
'country' => [
'type' => 'varchar',
'length' => 3,
'not null' => TRUE,
'default' => '',
],
'local_number' => [
'type' => 'varchar',
'length' => 15,
'not null' => TRUE,
'default' => '',
],
'verified' => [
'type' => 'int',
'not null' => TRUE,
'default' => 0,
],
'tfa' => [
'type' => 'int',
'not null' => TRUE,
'default' => 0,
],
],
'indexes' => [
'value' => [
'value',
],
],
];
}
public function isEmpty() {
$value = $this
->getValue();
return empty($value['value']) && empty($value['local_number']);
}
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('string')
->setLabel(t('E.165 Number'))
->addConstraint('Length', [
'max' => 19,
]);
$properties['country'] = DataDefinition::create('string')
->setLabel(t('Country Code'))
->addConstraint('Length', [
'max' => 3,
]);
$properties['local_number'] = DataDefinition::create('string')
->setLabel(t('National Number'))
->addConstraint('Length', [
'max' => 15,
]);
$properties['verified'] = DataDefinition::create('boolean')
->setLabel(t('Verified Status'));
$properties['tfa'] = DataDefinition::create('boolean')
->setLabel(t('TFA Option'));
return $properties;
}
public function preSave() {
$util = \Drupal::service('mobile_number.util');
$values = $this
->getValue();
$number = NULL;
$country = NULL;
if (!empty($values['country'])) {
if (!empty($values['local_number'])) {
$number = $values['local_number'];
}
$country = $values['country'];
}
if (!$number) {
$number = $values['value'];
}
if ($mobile_number = $util
->getMobileNumber($number, $country)) {
$this->value = $util
->getCallableNumber($mobile_number);
$this->country = $util
->getCountry($mobile_number);
$this->local_number = $util
->getLocalNumber($mobile_number);
$this->tfa = !empty($values['tfa']) ? 1 : 0;
$this->verified = $this
->verify() === TRUE ? 1 : 0;
}
else {
$this->value = NULL;
$this->local_number = NULL;
}
parent::preSave();
}
public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
$util = \Drupal::service('mobile_number.util');
$field = $form_state
->getFormObject()
->getEntity();
$element = [];
$element['unique'] = [
'#type' => 'radios',
'#title' => t('Unique'),
'#options' => [
$util::MOBILE_NUMBER_UNIQUE_NO => t('No'),
$util::MOBILE_NUMBER_UNIQUE_YES => t('Yes'),
$util::MOBILE_NUMBER_UNIQUE_YES_VERIFIED => t('Yes, only verified numbers'),
],
'#default_value' => $field
->getSetting('unique'),
'#description' => t('Should mobile numbers be unique within this field.'),
'#required' => TRUE,
];
return $element;
}
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
$util = \Drupal::service('mobile_number.util');
$field = $this
->getFieldDefinition();
$settings = $this
->getSettings() + $this
->defaultFieldSettings();
if ($form['#entity'] instanceof User && FALSE) {
$element['tfa'] = [
'#type' => 'checkbox',
'#title' => t('Use this field for two-factor authentication'),
'#description' => t("If enabled, users will be able to choose if to use the number for two factor authentication. Only one field can be set true for this value, verification must be enabled, and the field must be of cardinality 1. Users are required to verify their number when enabling their two-factor authenticaion. <a href='https://www.drupal.org/project/tfa' target='_blank'>Two Factor Authentication</a> must be installed, as well as a supported sms provider such as <a href='https://www.drupal.org/project/smsframework' target='_blank'>SMS Framework</a>."),
'#default_value' => $this
->tfaAllowed() && $util
->getTfaField() === $this
->getFieldDefinition()
->getName(),
'#disabled' => !$this
->tfaAllowed(),
];
if ($this
->tfaAllowed()) {
$element['tfa']['#states'] = [
'disabled' => [
'input[name="settings[verify]"]' => [
'value' => $util::MOBILE_NUMBER_VERIFY_NONE,
],
],
];
}
}
$element['countries'] = [
'#type' => 'select',
'#title' => t('Allowed Countries'),
'#options' => $util
->getCountryOptions([], TRUE),
'#default_value' => $this
->getSetting('countries'),
'#description' => t('Allowed countries for the mobile number. If none selected, then all are allowed.'),
'#multiple' => TRUE,
'#attached' => [
'library' => [
'mobile_number/element',
],
],
];
$element['verify'] = [
'#type' => 'radios',
'#title' => t('Verification'),
'#options' => [
MobileNumberUtilInterface::MOBILE_NUMBER_VERIFY_NONE => t('None'),
MobileNumberUtilInterface::MOBILE_NUMBER_VERIFY_OPTIONAL => t('Optional'),
MobileNumberUtilInterface::MOBILE_NUMBER_VERIFY_REQUIRED => t('Required'),
],
'#default_value' => $settings['verify'],
'#description' => (string) t('Verification requirement. Will send sms to mobile number when user requests to verify the number as their own. Requires <a href="https://www.drupal.org/project/smsframework" target="_blank">SMS Framework</a> or any other sms sending module that integrates with with the Mobile Number module.'),
'#required' => TRUE,
'#disabled' => !$util
->isSmsEnabled(),
];
$element['message'] = [
'#type' => 'textarea',
'#title' => t('Verification Message'),
'#default_value' => $settings['message'],
'#description' => t('The SMS message to send during verification. Replacement parameters are available for verification code (!code) and site name (!site_name). Additionally, tokens are available if the token module is enabled, but be aware that entity values will not be available on entity creation forms as the entity was not created yet.'),
'#required' => TRUE,
'#token_types' => [
$field
->getTargetEntityTypeId(),
],
'#disabled' => !$util
->isSmsEnabled(),
'#element_validate' => [
[
$this,
'fieldSettingsFormValidate',
],
],
];
if (\Drupal::moduleHandler()
->moduleExists('token')) {
$element['message']['#element_validate'] = [
'token_element_validate',
];
$element['message_token_tree']['token_tree'] = [
'#theme' => 'token_tree',
'#token_types' => [
$field
->getTargetEntityTypeId(),
],
'#dialog' => TRUE,
];
}
return $element;
}
public function fieldSettingsFormValidate(array $form, FormStateInterface $form_state) {
$submit_handlers = $form_state
->getSubmitHandlers();
$submit_handlers[] = [
$this,
'fieldSettingsFormSubmit',
];
$form_state
->setSubmitHandlers($submit_handlers);
}
public function fieldSettingsFormSubmit(array $form, FormStateInterface $form_state) {
$util = \Drupal::service('mobile_number.util');
$settings = $this
->getSettings();
if (!empty([
'message',
])) {
t($settings['message']);
}
$tfa = !empty($this
->getSetting('tfa'));
$field_name = $this
->getFieldDefinition()
->getName();
if (!empty($tfa)) {
$util
->setTfaField($field_name);
}
elseif ($field_name === $util
->getTfaField()) {
$util
->setTfaField('');
}
}
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
static $last_numbers = [];
$util = \Drupal::service('mobile_number.util');
$settings = [
'verify' => $util
->isSmsEnabled() ? $util::MOBILE_NUMBER_VERIFY_OPTIONAL : $util::MOBILE_NUMBER_VERIFY_NONE,
'countries' => [],
];
$country = array_rand($util
->getCountryOptions($settings['countries']));
$last = !empty($last_numbers[$country]) ? $last_numbers[$country] : [];
$mobile_number = NULL;
if (!$last) {
$last['count'] = 0;
$last['example'] = ($number = $util
->libUtil()
->getExampleNumberForType($country, 1)) ? $number
->getNationalNumber() : NULL;
}
$example = $last['example'];
$count = $last['count'];
if ($example) {
while (strlen($count) <= strlen($example) && !$mobile_number) {
$number_length = strlen($example);
$number = substr($example, 0, $number_length - strlen($count)) . $count;
if (substr($count, 0, 1) != substr($example, strlen($count) - 1, 1)) {
$mobile_number = $util
->getMobileNumber($number, $country);
}
$count = ($count + 1) % pow(10, strlen($example));
}
}
$value = [];
if ($mobile_number) {
$value = [
'value' => $util
->getCallableNumber($mobile_number),
];
switch ($settings['verify']) {
case $util::MOBILE_NUMBER_VERIFY_NONE:
$value['verified'] = 0;
break;
case $util::MOBILE_NUMBER_VERIFY_OPTIONAL:
$value['verified'] = rand(0, 1);
break;
case $util::MOBILE_NUMBER_VERIFY_REQUIRED:
$value['verified'] = 1;
break;
}
}
return $value;
}
public function tfaAllowed() {
$util = \Drupal::service('mobile_number.util');
return $util
->isTfaEnabled() && $this
->getFieldDefinition()
->getFieldStorageDefinition()
->getCardinality() == 1;
}
public function getMobileNumber($throw_exception = FALSE) {
$util = \Drupal::service('mobile_number.util');
$values = $this
->getValue();
$number = '';
$country = NULL;
if (!empty($values['country'])) {
if (!empty($values['local_number'])) {
$number = $values['local_number'];
}
$country = $values['country'];
}
if (!$number && !empty($values['value'])) {
$number = $values['value'];
}
if ($throw_exception) {
return $util
->testMobileNumber($number, $country);
}
else {
return $util
->getMobileNumber($number, $country);
}
}
public function isVerified() {
$util = \Drupal::service('mobile_number.util');
$input = $this
->getValue();
$field_name = $this
->getFieldDefinition()
->getName();
$field_label = $this
->getFieldDefinition()
->getLabel();
$entity = $this
->getEntity();
$entity_type = $entity
->getEntityType()
->getSingularLabel();
$entity_type_id = $entity
->getEntityTypeId();
$id_key = $entity
->getEntityType()
->getKey('id');
$mobile_number = $this
->getMobileNumber();
if (!$mobile_number) {
return FALSE;
}
$verified = (bool) \Drupal::entityQuery($entity_type_id)
->condition($id_key, (int) $entity
->id())
->condition($field_name, $util
->getCallableNumber($mobile_number))
->range(0, 1)
->condition("{$field_name}.verified", "1")
->count()
->execute();
$verified = $verified || $util
->isVerified($mobile_number);
return $verified;
}
public function verify() {
$util = \Drupal::service('mobile_number.util');
$values = $this
->getValue();
$token = !empty($values['verification_token']) ? $values['verification_token'] : NULL;
$code = !empty($values['verification_code']) ? $values['verification_code'] : NULL;
if ($this
->isVerified()) {
return TRUE;
}
$mobile_number = $this
->getMobileNumber();
if (!empty($token) && !empty($code) && $mobile_number) {
if ($util
->checkFlood($mobile_number)) {
return $util
->verifyCode($mobile_number, $code, $token);
}
else {
return -1;
}
}
else {
return NULL;
}
}
public function isUnique($unique_type = MobileNumberUtilInterface::MOBILE_NUMBER_UNIQUE_YES) {
$util = \Drupal::service('mobile_number.util');
$entity = $this
->getEntity();
$field_name = $this
->getFieldDefinition()
->getName();
if (!($mobile_number = $this
->getMobileNumber())) {
return NULL;
}
$entity_type_id = $entity
->getEntityTypeId();
$id_key = $entity
->getEntityType()
->getKey('id');
$query = \Drupal::entityQuery($entity_type_id)
->condition($id_key, (int) $entity
->id(), '<>')
->condition($field_name, $util
->getCallableNumber($mobile_number))
->range(0, 1)
->count();
if ($unique_type == MobileNumberUtilInterface::MOBILE_NUMBER_UNIQUE_YES_VERIFIED) {
$query
->condition("{$field_name}.verified", "1");
if ($this
->isVerified()) {
$result = !(bool) $query
->execute();
}
else {
$result = TRUE;
}
}
else {
$result = !(bool) $query
->execute();
}
return $result;
}
public static function countryOptions() {
$util = \Drupal::service('mobile_number.util');
return $util
->getCountryOptions([], TRUE);
}
public static function booleanOptions() {
return [
t('No'),
t('Yes'),
];
}
}