From 3f8018060e61ebc108e3517ad8a93d86a7839551 Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Tue, 18 Apr 2017 18:43:40 +0200 Subject: [PATCH] Split validation into multiple methods to simplify login and registration form customizations --- .../Plugin/Commerce/CheckoutPane/Login.php | 222 ++++++++++++------ 1 file changed, 153 insertions(+), 69 deletions(-) diff --git a/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php b/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php index 90654a54ab..594239aed3 100644 --- a/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php +++ b/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php @@ -11,6 +11,7 @@ use Drupal\Core\Url; use Drupal\Core\Link; use Drupal\user\UserAuthInterface; +use Drupal\user\UserInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RequestStack; @@ -195,7 +196,7 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, '#type' => 'textfield', '#title' => $this->t('Username'), '#size' => 60, - '#maxlength' => USERNAME_MAX_LENGTH, + '#maxlength' => UserInterface::USERNAME_MAX_LENGTH, '#attributes' => [ 'autocorrect' => 'none', 'autocapitalize' => 'none', @@ -259,7 +260,7 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, $pane_form['register']['name'] = [ '#type' => 'textfield', '#title' => $this->t('Username'), - '#maxlength' => USERNAME_MAX_LENGTH, + '#maxlength' => UserInterface::USERNAME_MAX_LENGTH, '#description' => $this->t("Several special characters are allowed, including space, period (.), hyphen (-), apostrophe ('), underscore (_), and the @ sign."), '#required' => FALSE, '#attributes' => [ @@ -289,87 +290,170 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, * {@inheritdoc} */ public function validatePaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) { - $values = $form_state->getValue($pane_form['#parents']); $triggering_element = $form_state->getTriggeringElement(); + switch ($triggering_element['#op']) { case 'continue': return; case 'login': - $name_element = $pane_form['returning_customer']['name']; - $username = $values['returning_customer']['name']; - $password = trim($values['returning_customer']['password']); - // Generate the "reset password" url. - $query = !empty($username) ? ['name' => $username] : []; - $password_url = Url::fromRoute('user.pass', [], ['query' => $query])->toString(); - - if (empty($username) || empty($password)) { - $form_state->setError($pane_form['returning_customer'], $this->t('Unrecognized username or password. Have you forgotten your password?', [':url' => $password_url])); - return; - } - if (user_is_blocked($username)) { - $form_state->setError($name_element, $this->t('The username %name has not been activated or is blocked.', ['%name' => $username])); - return; - } - if (!$this->credentialsCheckFlood->isAllowedHost($this->clientIp)) { - $form_state->setErrorByName($name_element, $this->t('Too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or request a new password.', [':url' => Url::fromRoute('user.pass')])); - $this->credentialsCheckFlood->register($this->clientIp, $username); - return; - } - elseif (!$this->credentialsCheckFlood->isAllowedAccount($this->clientIp, $username)) { - $form_state->setErrorByName($name_element, $this->t('Too many failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.', [':url' => Url::fromRoute('user.pass')])); - $this->credentialsCheckFlood->register($this->clientIp, $username); - return; - } - - $uid = $this->userAuth->authenticate($username, $password); - if (!$uid) { - $this->credentialsCheckFlood->register($this->clientIp, $username); - $form_state->setError($name_element, $this->t('Unrecognized username or password. Have you forgotten your password?', [':url' => $password_url])); - } - $form_state->set('logged_in_uid', $uid); + $this->validateReturningCustomer($pane_form, $form_state, $complete_form); break; case 'register': - $email = $values['register']['mail']; - $username = $values['register']['name']; - $password = trim($values['register']['password']); - if (empty($email)) { - $form_state->setError($pane_form['register']['mail'], $this->t('Email field is required.')); - return; - } - if (empty($username)) { - $form_state->setError($pane_form['register']['name'], $this->t('Username field is required.')); - return; - } - if (empty($password)) { - $form_state->setError($pane_form['register']['password'], $this->t('Password field is required.')); - return; - } + $this->validateRegister($pane_form, $form_state, $complete_form); - /** @var \Drupal\user\UserInterface $account */ - $account = $this->entityTypeManager->getStorage('user')->create([ - 'mail' => $email, - 'name' => $username, - 'pass' => $password, - 'status' => TRUE, - ]); - // Validate the entity. This will ensure that the username and email - // are in the right format and not already taken. - $violations = $account->validate(); - foreach ($violations->getByFields(['name', 'mail']) as $violation) { - list($field_name) = explode('.', $violation->getPropertyPath(), 2); - $form_state->setError($pane_form['register'][$field_name], $violation->getMessage()); - } - - if (!$form_state->hasAnyErrors()) { - $account->save(); - $form_state->set('logged_in_uid', $account->id()); - } break; } } + /** + * Validates a returning customers username and password. + * + * @param array $pane_form + * The pane form, containing the following basic properties: + * - #parents: Identifies the position of the pane form in the overall + * parent form, and identifies the location where the field values are + * placed within $form_state->getValues(). + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state of the parent form. + * @param array $complete_form + * The complete form structure. + */ + protected function validateReturningCustomer(array &$pane_form, FormStateInterface $form_state, array &$complete_form) { + $values = $form_state->getValue($pane_form['#parents']); + $name_element = $pane_form['returning_customer']['name']; + $username = $values['returning_customer']['name']; + $password = trim($values['returning_customer']['password']); + // Generate the "reset password" url. + $query = !empty($username) ? ['name' => $username] : []; + $password_url = Url::fromRoute('user.pass', [], ['query' => $query])->toString(); + + if (empty($username) || empty($password)) { + $form_state->setError($pane_form['returning_customer'], $this->t('Unrecognized username or password. Have you forgotten your password?', [':url' => $password_url])); + return; + } + if (user_is_blocked($username)) { + $form_state->setError($name_element, $this->t('The username %name has not been activated or is blocked.', ['%name' => $username])); + return; + } + if (!$this->credentialsCheckFlood->isAllowedHost($this->clientIp)) { + $form_state->setErrorByName($name_element, $this->t('Too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or request a new password.', [':url' => Url::fromRoute('user.pass')])); + $this->credentialsCheckFlood->register($this->clientIp, $username); + return; + } + elseif (!$this->credentialsCheckFlood->isAllowedAccount($this->clientIp, $username)) { + $form_state->setErrorByName($name_element, $this->t('Too many failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.', [':url' => Url::fromRoute('user.pass')])); + $this->credentialsCheckFlood->register($this->clientIp, $username); + return; + } + + $uid = $this->userAuth->authenticate($username, $password); + if (!$uid) { + $this->credentialsCheckFlood->register($this->clientIp, $username); + $form_state->setError($name_element, $this->t('Unrecognized username or password. Have you forgotten your password?', [':url' => $password_url])); + } + $form_state->set('logged_in_uid', $uid); + } + + /** + * Validates the registration form and creates a new user. + * + * @param array $pane_form + * The pane form, containing the following basic properties: + * - #parents: Identifies the position of the pane form in the overall + * parent form, and identifies the location where the field values are + * placed within $form_state->getValues(). + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state of the parent form. + * @param array $complete_form + * The complete form structure. + */ + protected function validateRegister(array &$pane_form, FormStateInterface $form_state, array &$complete_form) { + if ($user_values = $this->getAndValidateRegisterUserValues($pane_form, $form_state)) { + + /** @var \Drupal\user\UserInterface $account */ + $account = $this->entityTypeManager->getStorage('user')->create($user_values); + + // Validate the entity. This will ensure that the username and email + // are in the right format and not already taken. + $violations = $account->validate(); + foreach ($violations->getByFields(['name', 'mail']) as $violation) { + list($field_name) = explode('.', $violation->getPropertyPath(), 2); + $form_state->setError($pane_form['register'][$field_name], $violation->getMessage()); + } + + if (!$form_state->hasAnyErrors()) { + $account->save(); + $form_state->set('logged_in_uid', $account->id()); + } + } + } + + /** + * Validates form values for the register form. + * + * @param array $pane_form + * The pane form, containing the following basic properties: + * - #parents: Identifies the position of the pane form in the overall + * parent form, and identifies the location where the field values are + * placed within $form_state->getValues(). + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state of the parent form. + * + * @return array|null + * Either an array of values that can be used to create a new user or NULL + * if there are validation errors. + */ + protected function getAndValidateRegisterUserValues(array &$pane_form, FormStateInterface $form_state) { + $values = $form_state->getValue($pane_form['#parents']); + + $user_values = [ + 'status' => TRUE + ]; + + // If the e-mail, username and password fields are visible, ensure they + // have a value and add them to the user values. This allows subclasses and + // form alters to hide certain form elements and then provide alternative + // values, for example to set the username based on the e-mail. + $email_element = $pane_form['register']['mail']; + if (!isset($email_element['#access']) || $email_element['#access']) { + $email = $values['register']['mail']; + if (empty($email)) { + $form_state->setError($email_element, $this->t('Email field is required.')); + return NULL; + } + else { + $user_values['mail'] = $email; + } + } + + $name_element = $pane_form['register']['name']; + if (!isset($name_element['#access']) || $name_element['#access']) { + $username = $values['register']['name']; + if (empty($username)) { + $form_state->setError($name_element, $this->t('Username field is required.')); + return NULL; + } + else { + $user_values['name'] = $username; + } + } + + $password_element = $pane_form['register']['password']; + if (!isset($password_element['#access']) || $password_element['#access']) { + $password = trim($values['register']['password']); + if (empty($password)) { + $form_state->setError($pane_form['register']['password'], $this->t('Password field is required.')); + return NULL; + } + else { + $user_values['pass'] = $password; + } + } + return $user_values; + } + /** * {@inheritdoc} */