From 4595fa4fa299f497ed67b2ac7cb887fcf56c55a1 Mon Sep 17 00:00:00 2001 From: Harings Rob Date: Mon, 9 May 2016 13:23:38 +0200 Subject: [PATCH 1/8] 2716241 Registration on login pane --- modules/checkout/commerce_checkout.module | 25 +++++++++ .../schema/commerce_checkout.schema.yml | 3 ++ .../Plugin/Commerce/CheckoutPane/Login.php | 51 ++++++++++++++++++- 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/modules/checkout/commerce_checkout.module b/modules/checkout/commerce_checkout.module index 90e0753689..5a54968a98 100755 --- a/modules/checkout/commerce_checkout.module +++ b/modules/checkout/commerce_checkout.module @@ -9,6 +9,8 @@ use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; +use Drupal\Core\Url; +use Symfony\Component\HttpFoundation\RedirectResponse; /** * Implements hook_theme(). @@ -144,3 +146,26 @@ function commerce_checkout_line_item_views_form_submit($form, FormStateInterface $order_id = $form_state->getTriggeringElement()['#order_id']; $form_state->setRedirect('commerce_checkout.form', ['commerce_order' => $order_id]); } + +/** + * Implements hook_user_login(). + */ +function commerce_checkout_user_login($account) { + $route_match = \Drupal::getContainer()->get('current_route_match'); + if ($order = $route_match->getParameter('commerce_order')) { + // Make sure we redirect to the correct step. + $checkout_order_manager = \Drupal::getContainer()->get('commerce_checkout.checkout_order_manager'); + $checkout_flow = $checkout_order_manager->getCheckoutFlow($order); + $checkout_flow_plugin = $checkout_flow->getPlugin(); + + // Build the redirect target. + $redirect_target = Url::fromRoute('commerce_checkout.form', [ + 'commerce_order' => $order->getOrderNumber(), + 'step' => $checkout_flow_plugin->getNextStepId(), + ]); + + // Redirect. + $response = new RedirectResponse($redirect_target->toString()); + $response->send(); + } +} diff --git a/modules/checkout/config/schema/commerce_checkout.schema.yml b/modules/checkout/config/schema/commerce_checkout.schema.yml index e7d1c75dc3..a038db03d7 100644 --- a/modules/checkout/config/schema/commerce_checkout.schema.yml +++ b/modules/checkout/config/schema/commerce_checkout.schema.yml @@ -73,6 +73,9 @@ commerce_checkout.commerce_checkout_pane.login: allow_guest_checkout: type: boolean label: 'Allow guest checkout' + show_registration_form: + type: boolean + label: 'Show registration form' commerce_checkout_pane_configuration: type: mapping diff --git a/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php b/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php index bf4bef293c..e9ef903a1c 100644 --- a/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php +++ b/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php @@ -4,7 +4,9 @@ use Drupal\commerce\CredentialsCheckFloodInterface; use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowInterface; +use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Entity\EntityFormBuilderInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Session\AccountInterface; @@ -12,7 +14,6 @@ use Drupal\user\UserAuthInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RequestStack; - /** * Provides the login or continue pane. * @@ -76,17 +77,23 @@ class Login extends CheckoutPaneBase implements CheckoutPaneInterface, Container * The current user. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * The entity manager. + * @param \Drupal\Core\Entity\EntityFormBuilderInterface $entity_form_builder + * The entity form builder. * @param \Drupal\user\UserAuthInterface $user_auth * The user authentication object. * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack * The request stack. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, CheckoutFlowInterface $checkout_flow, CredentialsCheckFloodInterface $credentials_check_flood, AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, UserAuthInterface $user_auth, RequestStack $request_stack) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, CheckoutFlowInterface $checkout_flow, CredentialsCheckFloodInterface $credentials_check_flood, AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, EntityManagerInterface $entity_manager, EntityFormBuilderInterface $entity_form_builder, UserAuthInterface $user_auth, RequestStack $request_stack) { parent::__construct($configuration, $plugin_id, $plugin_definition, $checkout_flow); $this->credentialsCheckFlood = $credentials_check_flood; $this->currentUser = $current_user; $this->entityTypeManager = $entity_type_manager; + $this->entityManager = $entity_manager; + $this->entityFormBuilder = $entity_form_builder; $this->userAuth = $user_auth; $this->clientIp = $request_stack->getCurrentRequest()->getClientIp(); } @@ -103,6 +110,8 @@ public static function create(ContainerInterface $container, array $configuratio $container->get('commerce.credentials_check_flood'), $container->get('current_user'), $container->get('entity_type.manager'), + $container->get('entity.manager'), + $container->get('entity.form_builder'), $container->get('user.auth'), $container->get('request_stack') ); @@ -126,6 +135,12 @@ public function buildConfigurationSummary() { } else { $summary = $this->t('Guest checkout: Not allowed'); + if (!empty($this->configuration['show_registration_form'])) { + $summary .= '
' . $this->t('Registration form: Yes'); + } + else { + $summary .= '
' . $this->t('Registration form: No'); + } } return $summary; @@ -142,6 +157,13 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta '#default_value' => $this->configuration['allow_guest_checkout'], ]; + $form['show_registration_form'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Show registration form'), + '#description' => $this->t('If checked, a registration form will be presented if guest checkout is disabled.'), + '#default_value' => $this->configuration['show_registration_form'], + ]; + return $form; } @@ -154,6 +176,7 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s if (!$form_state->getErrors()) { $values = $form_state->getValue($form['#parents']); $this->configuration['allow_guest_checkout'] = !empty($values['allow_guest_checkout']); + $this->configuration['show_registration_form'] = !empty($values['show_registration_form']); } } @@ -226,6 +249,30 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, '#op' => 'continue', ]; + // Build the registration form if our config allows it. + if (!$this->configuration['allow_guest_checkout'] && $this->configuration['show_registration_form']) { + // Load the renderer service. + $renderer = \Drupal::service('renderer'); + + // Create a dummy account for the registration form. + $account = $this->entityManager->getStorage('user')->create([]); + // Load the registration form. + $form = $this->entityFormBuilder->getForm($account, 'register'); + + // Build a small form so we can output a fieldset. + $register_form['register'] = [ + '#type' => 'fieldset', + '#title' => $this->t('Create account'), + ]; + $register_form['register']['registration_form'] = [ + '#markup' => $renderer->render($form), + ]; + + // Attach it as a suffix to the complete form, this way it will remain a + // seperate form. + $complete_form['#suffix'] = \Drupal::service('renderer')->render($register_form); + } + return $pane_form; } From e9de3d4ea8529ae29e6e9dd85889e2b107b53840 Mon Sep 17 00:00:00 2001 From: Harings Rob Date: Mon, 9 May 2016 20:33:54 +0200 Subject: [PATCH 2/8] 2716241 removed depricated injection --- .../src/Plugin/Commerce/CheckoutPane/Login.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php b/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php index e9ef903a1c..ca9ddb4f12 100644 --- a/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php +++ b/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php @@ -4,7 +4,6 @@ use Drupal\commerce\CredentialsCheckFloodInterface; use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowInterface; -use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityFormBuilderInterface; use Drupal\Core\Form\FormStateInterface; @@ -14,6 +13,7 @@ use Drupal\user\UserAuthInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RequestStack; + /** * Provides the login or continue pane. * @@ -77,8 +77,6 @@ class Login extends CheckoutPaneBase implements CheckoutPaneInterface, Container * The current user. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. * @param \Drupal\Core\Entity\EntityFormBuilderInterface $entity_form_builder * The entity form builder. * @param \Drupal\user\UserAuthInterface $user_auth @@ -86,13 +84,12 @@ class Login extends CheckoutPaneBase implements CheckoutPaneInterface, Container * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack * The request stack. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, CheckoutFlowInterface $checkout_flow, CredentialsCheckFloodInterface $credentials_check_flood, AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, EntityManagerInterface $entity_manager, EntityFormBuilderInterface $entity_form_builder, UserAuthInterface $user_auth, RequestStack $request_stack) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, CheckoutFlowInterface $checkout_flow, CredentialsCheckFloodInterface $credentials_check_flood, AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, EntityFormBuilderInterface $entity_form_builder, UserAuthInterface $user_auth, RequestStack $request_stack) { parent::__construct($configuration, $plugin_id, $plugin_definition, $checkout_flow); $this->credentialsCheckFlood = $credentials_check_flood; $this->currentUser = $current_user; $this->entityTypeManager = $entity_type_manager; - $this->entityManager = $entity_manager; $this->entityFormBuilder = $entity_form_builder; $this->userAuth = $user_auth; $this->clientIp = $request_stack->getCurrentRequest()->getClientIp(); @@ -110,7 +107,6 @@ public static function create(ContainerInterface $container, array $configuratio $container->get('commerce.credentials_check_flood'), $container->get('current_user'), $container->get('entity_type.manager'), - $container->get('entity.manager'), $container->get('entity.form_builder'), $container->get('user.auth'), $container->get('request_stack') @@ -255,7 +251,7 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, $renderer = \Drupal::service('renderer'); // Create a dummy account for the registration form. - $account = $this->entityManager->getStorage('user')->create([]); + $account = $this->entityTypeManager->getStorage('user')->create([]); // Load the registration form. $form = $this->entityFormBuilder->getForm($account, 'register'); From 6009a77e9d4b52420df43a21c80261e18898a259 Mon Sep 17 00:00:00 2001 From: Harings Rob Date: Tue, 10 May 2016 19:21:05 +0200 Subject: [PATCH 3/8] 2716241 Improved implementation --- modules/checkout/commerce_checkout.module | 25 --- .../Plugin/Commerce/CheckoutPane/Login.php | 190 ++++++++++++------ 2 files changed, 128 insertions(+), 87 deletions(-) diff --git a/modules/checkout/commerce_checkout.module b/modules/checkout/commerce_checkout.module index 5a54968a98..90e0753689 100755 --- a/modules/checkout/commerce_checkout.module +++ b/modules/checkout/commerce_checkout.module @@ -9,8 +9,6 @@ use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; -use Drupal\Core\Url; -use Symfony\Component\HttpFoundation\RedirectResponse; /** * Implements hook_theme(). @@ -146,26 +144,3 @@ function commerce_checkout_line_item_views_form_submit($form, FormStateInterface $order_id = $form_state->getTriggeringElement()['#order_id']; $form_state->setRedirect('commerce_checkout.form', ['commerce_order' => $order_id]); } - -/** - * Implements hook_user_login(). - */ -function commerce_checkout_user_login($account) { - $route_match = \Drupal::getContainer()->get('current_route_match'); - if ($order = $route_match->getParameter('commerce_order')) { - // Make sure we redirect to the correct step. - $checkout_order_manager = \Drupal::getContainer()->get('commerce_checkout.checkout_order_manager'); - $checkout_flow = $checkout_order_manager->getCheckoutFlow($order); - $checkout_flow_plugin = $checkout_flow->getPlugin(); - - // Build the redirect target. - $redirect_target = Url::fromRoute('commerce_checkout.form', [ - 'commerce_order' => $order->getOrderNumber(), - 'step' => $checkout_flow_plugin->getNextStepId(), - ]); - - // Redirect. - $response = new RedirectResponse($redirect_target->toString()); - $response->send(); - } -} diff --git a/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php b/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php index ca9ddb4f12..d94d941bad 100644 --- a/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php +++ b/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php @@ -10,6 +10,7 @@ use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; +use Drupal\Core\Link; use Drupal\user\UserAuthInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RequestStack; @@ -119,6 +120,7 @@ public static function create(ContainerInterface $container, array $configuratio public function defaultConfiguration() { return [ 'allow_guest_checkout' => TRUE, + 'show_registration_form' => FALSE, ] + parent::defaultConfiguration(); } @@ -216,12 +218,15 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, '#title' => $this->t('Password'), '#size' => 60, ]; - // @todo Add a "forgotten password" link. $pane_form['returning_customer']['submit'] = [ '#type' => 'submit', '#value' => $this->t('Log in'), '#op' => 'login', ]; + $pane_form['returning_customer']['forgot_password'] = [ + '#type' => 'markup', + '#markup' => Link::createFromRoute($this->t('Forgot password?'), 'user.pass')->toString(), + ]; $pane_form['guest'] = [ '#type' => 'fieldset', @@ -245,29 +250,28 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, '#op' => 'continue', ]; - // Build the registration form if our config allows it. - if (!$this->configuration['allow_guest_checkout'] && $this->configuration['show_registration_form']) { - // Load the renderer service. - $renderer = \Drupal::service('renderer'); - - // Create a dummy account for the registration form. - $account = $this->entityTypeManager->getStorage('user')->create([]); - // Load the registration form. - $form = $this->entityFormBuilder->getForm($account, 'register'); - - // Build a small form so we can output a fieldset. - $register_form['register'] = [ - '#type' => 'fieldset', - '#title' => $this->t('Create account'), - ]; - $register_form['register']['registration_form'] = [ - '#markup' => $renderer->render($form), - ]; - - // Attach it as a suffix to the complete form, this way it will remain a - // seperate form. - $complete_form['#suffix'] = \Drupal::service('renderer')->render($register_form); - } + $pane_form['register'] = [ + '#type' => 'fieldset', + '#title' => $this->t('Create new account'), + '#access' => !$this->configuration['allow_guest_checkout'] && $this->configuration['show_registration_form'], + ]; + $pane_form['register']['mail'] = array( + '#type' => 'email', + '#title' => $this->t('Email address'), + '#description' => $this->t('A valid email address. All emails from the system will be sent to this address. The email address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by email.'), + '#required' => FALSE, + ); + $pane_form['register']['pass'] = array( + '#type' => 'password_confirm', + '#size' => 25, + '#description' => $this->t('Provide a password for the new account in both fields.'), + '#required' => FALSE, + ); + $pane_form['register']['register'] = [ + '#type' => 'submit', + '#value' => $this->t('Create account and continue'), + '#op' => 'register', + ]; return $pane_form; } @@ -277,40 +281,91 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, */ public function validatePaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) { $triggering_element = $form_state->getTriggeringElement(); - if ($triggering_element['#op'] == 'continue') { - // No login in progress, nothing to validate. - return; - } - $name_element = $pane_form['returning_customer']['name']; - $values = $form_state->getValue($pane_form['#parents']); - $username = $values['returning_customer']['name']; - $password = trim($values['returning_customer']['password']); - if (empty($username) || empty($password)) { - $form_state->setErrorByName('name', $this->t('Unrecognized username or password.')); - 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; + switch ($triggering_element['#op']) { + case 'continue': + // No login in progress, nothing to validate. + return; + + case 'login': + $name_element = $pane_form['returning_customer']['name']; + $values = $form_state->getValue($pane_form['#parents']); + $username = $values['returning_customer']['name']; + $password = trim($values['returning_customer']['password']); + if (empty($username) || empty($password)) { + $form_state->setErrorByName('name', $this->t('Unrecognized username or password.')); + 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->setErrorByName('name', $this->t('Unrecognized username or password.')); + } + $form_state->set('logged_in_uid', $uid); + break; + + case 'register': + $values = $form_state->getValue($pane_form['#parents']); + + // Basic validation to check if fields are filled in. + if (empty($values['register']['mail'])) { + $form_state->setErrorByName('mail', $this->t('Email is mandatory.')); + return; + } + if (empty($values['register']['pass'])) { + $form_state->setErrorByName('pass', $this->t('Password is mandatory.')); + return; + } + + // Advanced validation Make sure the account does not exist yet. And + // that the username is unused/valid. + if ($this->entityTypeManager->getStorage('user')->loadByProperties(['mail' => $values['register']['mail']])) { + $form_state->setErrorByName('mail', $this->t('A user is already registered with this email.')); + return; + } + if ($this->entityTypeManager->getStorage('user')->loadByProperties(['name' => $values['register']['mail']])) { + $form_state->setErrorByName('mail', $this->t('A user is already registered with this username, please contact support to resolve this issue.')); + return; + } + // Make sure the email would be a valid username. + if (user_validate_name($values['register']['mail'])) { + $form_state->setErrorByName('mail', $this->t('The email you have used contains bad characters.')); + return; + } + + // Create the new account. + $account = $this->entityTypeManager->getStorage('user')->create([]); + $account->setEmail($values['register']['mail']); + $account->setUsername($values['register']['mail']); + $account->setPassword($values['register']['pass']); + $account->enforceIsNew(); + $account->activate(); + $account->save(); + + // Login. + $form_state->set('logged_in_uid', $account->id()); + drupal_set_message($this->t('Registration successful. You can now continue the checkout.')); + break; + + default: + $form_state->setError($pane_form['returning_customer']['name'], $this->t('Invalid submission, please submit the form again.')); + break; } - - $uid = $this->userAuth->authenticate($username, $password); - if (!$uid) { - $this->credentialsCheckFlood->register($this->clientIp, $username); - $form_state->setErrorByName('name', $this->t('Unrecognized username or password.')); - } - $form_state->set('logged_in_uid', $uid); } /** @@ -318,12 +373,23 @@ public function validatePaneForm(array &$pane_form, FormStateInterface $form_sta */ public function submitPaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) { $triggering_element = $form_state->getTriggeringElement(); - if ($triggering_element['#op'] == 'login') { - $storage = $this->entityTypeManager->getStorage('user'); - $account = $storage->load($form_state->get('logged_in_uid')); - user_login_finalize($account); - $this->order->setOwner($account); - $this->credentialsCheckFlood->clearAccount($this->clientIp, $account->getAccountName()); + + switch ($triggering_element['#op']) { + case 'login': + case 'register': + $storage = $this->entityTypeManager->getStorage('user'); + /** @var \Drupal\user\UserInterface $account */ + $account = $storage->load($form_state->get('logged_in_uid')); + user_login_finalize($account); + $this->order->setOwner($account); + $this->credentialsCheckFlood->clearAccount($this->clientIp, $account->getAccountName()); + break; + + case 'continue': + break; + + default: + return; } $form_state->setRedirect('commerce_checkout.form', [ From 1dc94d86ffadb4984bca04cf19d5aa1f772f6489 Mon Sep 17 00:00:00 2001 From: Harings Rob Date: Wed, 22 Jun 2016 14:39:05 +0200 Subject: [PATCH 4/8] 2716241: added additional classes --- modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php b/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php index d94d941bad..acd1c08282 100644 --- a/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php +++ b/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php @@ -254,6 +254,12 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, '#type' => 'fieldset', '#title' => $this->t('Create new account'), '#access' => !$this->configuration['allow_guest_checkout'] && $this->configuration['show_registration_form'], + '#attributes' => [ + 'class' => [ + 'form-wrapper__login-option', + 'form-wrapper__guest-checkout', + ], + ], ]; $pane_form['register']['mail'] = array( '#type' => 'email', From bd0898845d5c436e228b736bb808010e0cdc7845 Mon Sep 17 00:00:00 2001 From: Harings Rob Date: Wed, 22 Jun 2016 14:45:54 +0200 Subject: [PATCH 5/8] 2716241: Resolved other issues pointed out by bojanz --- .../src/Plugin/Commerce/CheckoutPane/Login.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php b/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php index acd1c08282..de8773c222 100644 --- a/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php +++ b/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php @@ -261,18 +261,18 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, ], ], ]; - $pane_form['register']['mail'] = array( + $pane_form['register']['mail'] = [ '#type' => 'email', '#title' => $this->t('Email address'), '#description' => $this->t('A valid email address. All emails from the system will be sent to this address. The email address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by email.'), '#required' => FALSE, - ); - $pane_form['register']['pass'] = array( + ]; + $pane_form['register']['pass'] = [ '#type' => 'password_confirm', '#size' => 25, '#description' => $this->t('Provide a password for the new account in both fields.'), '#required' => FALSE, - ); + ]; $pane_form['register']['register'] = [ '#type' => 'submit', '#value' => $this->t('Create account and continue'), @@ -340,11 +340,12 @@ public function validatePaneForm(array &$pane_form, FormStateInterface $form_sta // Advanced validation Make sure the account does not exist yet. And // that the username is unused/valid. - if ($this->entityTypeManager->getStorage('user')->loadByProperties(['mail' => $values['register']['mail']])) { + $user_storage = $this->entityTypeManager->getStorage('user'); + if ($user_storage->loadByProperties(['mail' => $values['register']['mail']])) { $form_state->setErrorByName('mail', $this->t('A user is already registered with this email.')); return; } - if ($this->entityTypeManager->getStorage('user')->loadByProperties(['name' => $values['register']['mail']])) { + if ($user_storage->loadByProperties(['name' => $values['register']['mail']])) { $form_state->setErrorByName('mail', $this->t('A user is already registered with this username, please contact support to resolve this issue.')); return; } @@ -391,9 +392,6 @@ public function submitPaneForm(array &$pane_form, FormStateInterface $form_state $this->credentialsCheckFlood->clearAccount($this->clientIp, $account->getAccountName()); break; - case 'continue': - break; - default: return; } From 1687a05e9305e3cf0ae720ff29edcd8d328f38fc Mon Sep 17 00:00:00 2001 From: Harings Rob Date: Wed, 22 Jun 2016 18:15:49 +0200 Subject: [PATCH 6/8] 2716241: Added test for registration form --- ...heckout.commerce_checkout_flow.default.yml | 1 + .../src/Functional/CheckoutOrderTest.php | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/modules/checkout/config/install/commerce_checkout.commerce_checkout_flow.default.yml b/modules/checkout/config/install/commerce_checkout.commerce_checkout_flow.default.yml index 7d5c31b25a..a36037b307 100644 --- a/modules/checkout/config/install/commerce_checkout.commerce_checkout_flow.default.yml +++ b/modules/checkout/config/install/commerce_checkout.commerce_checkout_flow.default.yml @@ -10,6 +10,7 @@ configuration: panes: login: allow_guest_checkout: true + show_registration_form: false step: login weight: 0 contact_information: diff --git a/modules/checkout/tests/src/Functional/CheckoutOrderTest.php b/modules/checkout/tests/src/Functional/CheckoutOrderTest.php index e34b325d9d..d22ec026fa 100644 --- a/modules/checkout/tests/src/Functional/CheckoutOrderTest.php +++ b/modules/checkout/tests/src/Functional/CheckoutOrderTest.php @@ -90,4 +90,30 @@ public function testGuestOrderCheckout() { $this->assertSession()->pageTextContains('Your order number is 1. You can view your order on your account page when logged in.'); } + /** + * Tests that you can register from the checkout pane. + */ + public function testRegisterOrderCheckout() { + // First we enable the checkout registration. + $config = \Drupal::configFactory()->getEditable('commerce_checkout.commerce_checkout_flow.default'); + $config->set('configuration.panes.login.allow_guest_checkout', FALSE); + $config->set('configuration.panes.login.show_registration_form', TRUE); + $config->save(); + + $this->drupalLogout(); + $this->drupalGet($this->product->toUrl()->toString()); + $this->submitForm([], 'Add to cart'); + $cart_link = $this->getSession()->getPage()->findLink('your cart'); + $cart_link->click(); + $this->submitForm([], 'Checkout'); + $this->assertSession()->pageTextContains('Create new account'); + $this->submitForm([ + 'login[register][mail]' => 'guest@example.com', + 'login[register][pass][pass1]' => 'pass', + 'login[register][pass][pass2]' => 'pass', + ], 'Create account and continue'); + $this->assertSession()->pageTextContains('Registration successful. You can now continue the checkout.'); + $this->assertSession()->pageTextContains('Billing information'); + } + } From 03472b4626aebe3b2be52bf4645c04a6951f5d31 Mon Sep 17 00:00:00 2001 From: Harings Rob Date: Thu, 23 Jun 2016 11:02:56 +0200 Subject: [PATCH 7/8] 2716241: Better test coverage for registration form --- .../src/Functional/CheckoutOrderTest.php | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/modules/checkout/tests/src/Functional/CheckoutOrderTest.php b/modules/checkout/tests/src/Functional/CheckoutOrderTest.php index d22ec026fa..06c1bd4aef 100644 --- a/modules/checkout/tests/src/Functional/CheckoutOrderTest.php +++ b/modules/checkout/tests/src/Functional/CheckoutOrderTest.php @@ -114,6 +114,48 @@ public function testRegisterOrderCheckout() { ], 'Create account and continue'); $this->assertSession()->pageTextContains('Registration successful. You can now continue the checkout.'); $this->assertSession()->pageTextContains('Billing information'); + + // Test various validations. We first redo the same as above to emulate a + // double registration. + $this->drupalLogout(); + $this->drupalGet($this->product->toUrl()->toString()); + $this->submitForm([], 'Add to cart'); + $cart_link = $this->getSession()->getPage()->findLink('your cart'); + $cart_link->click(); + $this->submitForm([], 'Checkout'); + $this->assertSession()->pageTextContains('Create new account'); + + // Already used e-mail. + $this->submitForm([ + 'login[register][mail]' => 'guest@example.com', + 'login[register][pass][pass1]' => 'pass', + 'login[register][pass][pass2]' => 'pass', + ], 'Create account and continue'); + $this->assertSession()->pageTextContains('A user is already registered with this email.'); + + // Invalid characters. + $this->submitForm([ + 'login[register][mail]' => 'guest@#.com', + 'login[register][pass][pass1]' => 'pass', + 'login[register][pass][pass2]' => 'pass', + ], 'Create account and continue'); + $this->assertSession()->pageTextContains('The email you have used contains bad characters.'); + + // Empty e-mail. + $this->submitForm([ + 'login[register][mail]' => '', + 'login[register][pass][pass1]' => 'pass', + 'login[register][pass][pass2]' => 'pass', + ], 'Create account and continue'); + $this->assertSession()->pageTextContains('Email is mandatory.'); + + // Empty password. + $this->submitForm([ + 'login[register][mail]' => 'valid@example.com', + 'login[register][pass][pass1]' => '', + 'login[register][pass][pass2]' => '', + ], 'Create account and continue'); + $this->assertSession()->pageTextContains('Password is mandatory.'); } } From 82bd110ba6393bfe93e9fc614122766088ae5d37 Mon Sep 17 00:00:00 2001 From: Harings Rob Date: Fri, 24 Jun 2016 13:38:57 +0200 Subject: [PATCH 8/8] 2716241: Improved display and logic --- .../checkout/css/commerce_checkout.layout.css | 3 +- .../Plugin/Commerce/CheckoutPane/Login.php | 38 ++++++++++--------- .../src/Functional/CheckoutOrderTest.php | 2 +- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/modules/checkout/css/commerce_checkout.layout.css b/modules/checkout/css/commerce_checkout.layout.css index d7c9481368..e240028b15 100644 --- a/modules/checkout/css/commerce_checkout.layout.css +++ b/modules/checkout/css/commerce_checkout.layout.css @@ -11,7 +11,8 @@ box-sizing: border-box; } -.form-wrapper__returning-customer input:not([type="submit"]) { +.form-wrapper__returning-customer input:not([type="submit"]), +.form-wrapper__login-option input[type="email"] { width: 100%; } diff --git a/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php b/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php index de8773c222..00beacca49 100644 --- a/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php +++ b/modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php @@ -120,7 +120,7 @@ public static function create(ContainerInterface $container, array $configuratio public function defaultConfiguration() { return [ 'allow_guest_checkout' => TRUE, - 'show_registration_form' => FALSE, + 'allow_registration' => FALSE, ] + parent::defaultConfiguration(); } @@ -128,17 +128,12 @@ public function defaultConfiguration() { * {@inheritdoc} */ public function buildConfigurationSummary() { + $summary = $this->t('Login allowed'); if (!empty($this->configuration['allow_guest_checkout'])) { - $summary = $this->t('Guest checkout: Allowed'); + $summary .= '
' . $this->t('Guest checkout allowed'); } - else { - $summary = $this->t('Guest checkout: Not allowed'); - if (!empty($this->configuration['show_registration_form'])) { - $summary .= '
' . $this->t('Registration form: Yes'); - } - else { - $summary .= '
' . $this->t('Registration form: No'); - } + if (!empty($this->configuration['allow_registration'])) { + $summary .= '
' . $this->t('Registration allowed'); } return $summary; @@ -153,13 +148,22 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta '#type' => 'checkbox', '#title' => $this->t('Allow guest checkout'), '#default_value' => $this->configuration['allow_guest_checkout'], + '#states' => [ + 'visible' => [ + ':input[name="configuration[panes][login][configuration][allow_registration]"]' => ['checked' => FALSE], + ], + ], ]; - $form['show_registration_form'] = [ + $form['allow_registration'] = [ '#type' => 'checkbox', - '#title' => $this->t('Show registration form'), - '#description' => $this->t('If checked, a registration form will be presented if guest checkout is disabled.'), - '#default_value' => $this->configuration['show_registration_form'], + '#title' => $this->t('Allow registration'), + '#default_value' => $this->configuration['allow_registration'], + '#states' => [ + 'visible' => [ + ':input[name="configuration[panes][login][configuration][allow_guest_checkout]"]' => ['checked' => FALSE], + ], + ], ]; return $form; @@ -174,7 +178,7 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s if (!$form_state->getErrors()) { $values = $form_state->getValue($form['#parents']); $this->configuration['allow_guest_checkout'] = !empty($values['allow_guest_checkout']); - $this->configuration['show_registration_form'] = !empty($values['show_registration_form']); + $this->configuration['allow_registration'] = !empty($values['allow_registration']); } } @@ -253,7 +257,7 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, $pane_form['register'] = [ '#type' => 'fieldset', '#title' => $this->t('Create new account'), - '#access' => !$this->configuration['allow_guest_checkout'] && $this->configuration['show_registration_form'], + '#access' => $this->configuration['allow_registration'], '#attributes' => [ 'class' => [ 'form-wrapper__login-option', @@ -269,7 +273,7 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, ]; $pane_form['register']['pass'] = [ '#type' => 'password_confirm', - '#size' => 25, + '#size' => 60, '#description' => $this->t('Provide a password for the new account in both fields.'), '#required' => FALSE, ]; diff --git a/modules/checkout/tests/src/Functional/CheckoutOrderTest.php b/modules/checkout/tests/src/Functional/CheckoutOrderTest.php index 06c1bd4aef..5a59510a19 100644 --- a/modules/checkout/tests/src/Functional/CheckoutOrderTest.php +++ b/modules/checkout/tests/src/Functional/CheckoutOrderTest.php @@ -97,7 +97,7 @@ public function testRegisterOrderCheckout() { // First we enable the checkout registration. $config = \Drupal::configFactory()->getEditable('commerce_checkout.commerce_checkout_flow.default'); $config->set('configuration.panes.login.allow_guest_checkout', FALSE); - $config->set('configuration.panes.login.show_registration_form', TRUE); + $config->set('configuration.panes.login.allow_registration', TRUE); $config->save(); $this->drupalLogout();