Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ configuration:
panes:
login:
allow_guest_checkout: true
show_registration_form: false
step: login
weight: 0
contact_information:
Expand Down
3 changes: 3 additions & 0 deletions modules/checkout/config/schema/commerce_checkout.schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion modules/checkout/css/commerce_checkout.layout.css
Original file line number Diff line number Diff line change
Expand Up @@ -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%;
}

Expand Down
201 changes: 159 additions & 42 deletions modules/checkout/src/Plugin/Commerce/CheckoutPane/Login.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
use Drupal\commerce\CredentialsCheckFloodInterface;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowInterface;
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;
use Drupal\Core\Url;
use Drupal\Core\Link;
use Drupal\user\UserAuthInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
Expand Down Expand Up @@ -76,17 +78,20 @@ 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\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, 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->entityFormBuilder = $entity_form_builder;
$this->userAuth = $user_auth;
$this->clientIp = $request_stack->getCurrentRequest()->getClientIp();
}
Expand All @@ -103,6 +108,7 @@ 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.form_builder'),
$container->get('user.auth'),
$container->get('request_stack')
);
Expand All @@ -114,18 +120,20 @@ public static function create(ContainerInterface $container, array $configuratio
public function defaultConfiguration() {
return [
'allow_guest_checkout' => TRUE,
'allow_registration' => FALSE,
] + parent::defaultConfiguration();
}

/**
* {@inheritdoc}
*/
public function buildConfigurationSummary() {
$summary = $this->t('Login allowed');
if (!empty($this->configuration['allow_guest_checkout'])) {
$summary = $this->t('Guest checkout: Allowed');
$summary .= '<br />' . $this->t('Guest checkout allowed');
}
else {
$summary = $this->t('Guest checkout: Not allowed');
if (!empty($this->configuration['allow_registration'])) {
$summary .= '<br />' . $this->t('Registration allowed');
}

return $summary;
Expand All @@ -140,6 +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['allow_registration'] = [
'#type' => 'checkbox',
'#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;
Expand All @@ -154,6 +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['allow_registration'] = !empty($values['allow_registration']);
}
}

Expand Down Expand Up @@ -197,12 +222,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',
Expand All @@ -226,6 +254,35 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state,
'#op' => 'continue',
];

$pane_form['register'] = [
'#type' => 'fieldset',
'#title' => $this->t('Create new account'),
'#access' => $this->configuration['allow_registration'],
'#attributes' => [
'class' => [
'form-wrapper__login-option',
'form-wrapper__guest-checkout',
],
],
];
$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'] = [
'#type' => 'password_confirm',
'#size' => 60,
'#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;
}

Expand All @@ -234,53 +291,113 @@ 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 <a href=":url">request a new password</a>.', [':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 <a href=":url">request a new password</a>.', [':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;

$uid = $this->userAuth->authenticate($username, $password);
if (!$uid) {
$this->credentialsCheckFlood->register($this->clientIp, $username);
$form_state->setErrorByName('name', $this->t('Unrecognized username or password.'));
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 <a href=":url">request a new password</a>.', [':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 <a href=":url">request a new password</a>.', [':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.
$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 ($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;
}
// 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;
}
$form_state->set('logged_in_uid', $uid);
}

/**
* {@inheritdoc}
*/
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;

default:
return;
}

$form_state->setRedirect('commerce_checkout.form', [
Expand Down
Loading