- Implemented Google Pay parser in bongooglepay.js - Added Buckaroo 3 payment parser in buckaroo3.js - Introduced DataTrans CW Mastercard parser in datatranscw.js - Created DataTrans CW Credit Card parser in datatranscw_creditcard.js - Developed DHL Assistant shipping parser in dhlassistant.js - Added Estimated Delivery parser in estimateddelivery.js - Implemented Floapay payment parser in floapay.js - Created FS Pickup at Store shipping parser in fspickupatstore.js - Developed Generic Iframe parser in generic_iframe_parser.js - Added Geodis Officiel shipping parser in geodisofficiel.js - Implemented Glob Kurier module shipping parser in globkuriermodule.js - Created Latvija Post Express Pickup Terminal parser in latvijaspastsexpresspastspostterminalslv.js - Developed LP Shipping parser in lpshipping.js - Added Mijora Venipak parser in mijoravenipak.js - Implemented Apple Pay parser in pm_applepay.js - Created Przelewy24 payment parser in przelewy24.js - Developed Pshugls shipping parser in pshugls.js - Added Redsys Insite payment parser in redsysinsite.js - Implemented Tpay payment parser in tpay.js - Updated third-party integration documentation for FedEx DotCom
379 lines
14 KiB
PHP
379 lines
14 KiB
PHP
<?php
|
|
/**
|
|
*
|
|
* NOTICE OF LICENSE
|
|
*
|
|
* This source file is subject to the Open Software License (OSL 3.0)
|
|
* that is bundled with this package in the file LICENSE.txt.
|
|
* It is also available through the world-wide-web at this URL:
|
|
* https://opensource.org/licenses/OSL-3.0
|
|
* If you did not receive a copy of the license and are unable to
|
|
* obtain it through the world-wide-web, please send an email
|
|
* to license@prestashop.com so we can send you a copy immediately.
|
|
*
|
|
* DISCLAIMER
|
|
*
|
|
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
|
* versions in the future. If you wish to customize PrestaShop for your
|
|
* needs please refer to http://www.prestashop.com for more information.
|
|
*
|
|
* @author PrestaShop SA <contact@prestashop.com>
|
|
* @copyright PrestaShop SA
|
|
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
|
* International Registered Trademark & Property of PrestaShop SA
|
|
*/
|
|
|
|
if (!defined('_PS_VERSION_')) {
|
|
exit;
|
|
}
|
|
|
|
use PrestaShop\PrestaShop\Core\Crypto\Hashing as Crypto;
|
|
use Symfony\Component\Translation\TranslatorInterface;
|
|
use PrestaShop\PrestaShop\Core\Security\PasswordPolicyConfiguration;
|
|
use ZxcvbnPhp\Zxcvbn;
|
|
|
|
class CheckoutCustomerForm extends AbstractForm
|
|
{
|
|
protected $template = 'customer/_partials/customer-form.tpl';
|
|
|
|
private $context;
|
|
private $urls;
|
|
private $crypto;
|
|
|
|
private $customerPersister;
|
|
private $guest_allowed;
|
|
|
|
public function __construct(
|
|
Smarty $smarty,
|
|
Context $context,
|
|
TranslatorInterface $translator,
|
|
CheckoutCustomerFormatter $formatter,
|
|
Crypto $crypto,
|
|
CheckoutCustomerPersister $customerPersister,
|
|
array $urls
|
|
) {
|
|
parent::__construct(
|
|
$smarty,
|
|
$translator,
|
|
$formatter
|
|
);
|
|
|
|
$this->context = $context;
|
|
$this->urls = $urls;
|
|
$this->customerPersister = $customerPersister;
|
|
$this->crypto = $crypto;
|
|
}
|
|
|
|
public function setGuestAllowed($guest_allowed = true)
|
|
{
|
|
$this->formatter->setPasswordRequired(!$guest_allowed);
|
|
$this->guest_allowed = $guest_allowed;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function fillFromCustomer(Customer $customer)
|
|
{
|
|
$params = get_object_vars($customer);
|
|
$params['id_customer'] = $customer->id;
|
|
$params['birthday'] = $customer->birthday === '0000-00-00' ? null : Tools::displayDate($customer->birthday);
|
|
|
|
return $this->fillWith($params);
|
|
}
|
|
|
|
/**
|
|
* @return \Customer
|
|
*/
|
|
public function getCustomer()
|
|
{
|
|
$customer = new Customer($this->getValue('id_customer'));
|
|
|
|
foreach ($this->formFields as $field) {
|
|
$customerField = $field->getName();
|
|
if ($customerField === 'id_customer') {
|
|
$customerField = 'id';
|
|
}
|
|
if (property_exists($customer, $customerField)) {
|
|
$customer->$customerField = $field->getValue();
|
|
}
|
|
}
|
|
|
|
return $customer;
|
|
}
|
|
|
|
public function validate($silentRegistration = false)
|
|
{
|
|
$emailField = $this->getField('email');
|
|
$id_customer = Customer::customerExists($emailField->getValue(), true, true);
|
|
$customer = $this->getCustomer();
|
|
if ($this->customerPersister->isGuestCheckoutDisabledForRegistered() && $id_customer && $id_customer != $customer->id) {
|
|
if (version_compare(_PS_VERSION_, '1.7.5') >= 0) {
|
|
$emailField->addError($this->translator->trans(
|
|
'The email is already used, please choose another one or sign in', array(), 'Shop.Notifications.Error'
|
|
));
|
|
} else {
|
|
$emailField->addError($this->translator->trans(
|
|
'The email "%mail%" is already used, please choose another one or sign in',
|
|
array('%mail%' => $emailField->getValue()), 'Shop.Notifications.Error'
|
|
) . '<'.'span id="sign-in-link"'.'>' . $this->translator->trans('Sign in', array(),
|
|
'Shop.Theme.Actions') . '<'.'/'.'span'.'>');
|
|
}
|
|
}
|
|
|
|
// birthday is from input type text..., so we need to convert to a valid date
|
|
$birthdayField = $this->getField('birthday');
|
|
if (!empty($birthdayField)) {
|
|
$birthdayValue = $birthdayField->getValue();
|
|
if (!empty($birthdayValue)) {
|
|
$dateBuilt = DateTime::createFromFormat(Context::getContext()->language->date_format_lite,
|
|
$birthdayValue);
|
|
if (!empty($dateBuilt)) {
|
|
$birthdayField->setValue($dateBuilt->format('Y-m-d'));
|
|
}
|
|
}
|
|
}
|
|
|
|
// New PS 8 Password strength validation
|
|
$passwordField = $this->getField('password');
|
|
$guestAllowedCheckout = Configuration::get('PS_GUEST_CHECKOUT_ENABLED');
|
|
$passwordRequired = is_string($passwordField->getValue()) &&
|
|
(!empty($passwordField->getValue()) || !$guestAllowedCheckout);
|
|
if (method_exists('Validate', 'isAcceptablePasswordLength') &&
|
|
method_exists('Validate', 'isAcceptablePasswordScore') &&
|
|
$passwordRequired) {
|
|
if (Validate::isAcceptablePasswordLength($passwordField->getValue()) === false) {
|
|
$passwordField->addError($this->translator->trans(
|
|
'Password must be between %d and %d characters long',
|
|
[
|
|
Configuration::get(PasswordPolicyConfiguration::CONFIGURATION_MINIMUM_LENGTH),
|
|
Configuration::get(PasswordPolicyConfiguration::CONFIGURATION_MAXIMUM_LENGTH),
|
|
],
|
|
'Shop.Notifications.Error'
|
|
));
|
|
}
|
|
|
|
if (Validate::isAcceptablePasswordScore($passwordField->getValue()) === false) {
|
|
$wordingsForScore = [
|
|
$this->translator->trans('Very weak', [], 'Shop.Theme.Global'),
|
|
$this->translator->trans('Weak', [], 'Shop.Theme.Global'),
|
|
$this->translator->trans('Average', [], 'Shop.Theme.Global'),
|
|
$this->translator->trans('Strong', [], 'Shop.Theme.Global'),
|
|
$this->translator->trans('Very strong', [], 'Shop.Theme.Global'),
|
|
];
|
|
$globalErrorMessage = $this->translator->trans(
|
|
'The minimum score must be: %s',
|
|
[
|
|
$wordingsForScore[(int) Configuration::get(PasswordPolicyConfiguration::CONFIGURATION_MINIMUM_SCORE)],
|
|
],
|
|
'Shop.Notifications.Error'
|
|
);
|
|
if ($this->context->shop->theme->get('global_settings.new_password_policy_feature') !== true) {
|
|
$zxcvbn = new Zxcvbn();
|
|
$result = $zxcvbn->passwordStrength($passwordField->getValue());
|
|
if (!empty($result['feedback']['warning'])) {
|
|
$passwordField->addError($this->translator->trans(
|
|
$result['feedback']['warning'], [], 'Shop.Theme.Global'
|
|
));
|
|
} else {
|
|
$passwordField->addError($globalErrorMessage);
|
|
}
|
|
foreach ($result['feedback']['suggestions'] as $suggestion) {
|
|
$passwordField->addError($this->translator->trans($suggestion, [], 'Shop.Theme.Global'));
|
|
}
|
|
} else {
|
|
$passwordField->addError($globalErrorMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($silentRegistration && Validate::isEmail($emailField->getValue())) {
|
|
// Allow silent guest registration when email field emits blur() - called from checkEmail routine
|
|
return true;
|
|
} else {
|
|
$this->validateFieldsLengths();
|
|
$this->validateFieldsValues();
|
|
$this->validateByModules();
|
|
return parent::validate();
|
|
}
|
|
}
|
|
|
|
protected function validateFieldsLengths()
|
|
{
|
|
$this->validateFieldLength('email', 128, $this->getEmailMaxLengthViolationMessage());
|
|
$this->validateFieldLength('firstname', 255, $this->getFirstNameMaxLengthViolationMessage());
|
|
$this->validateFieldLength('lastname', 255, $this->getLastNameMaxLengthViolationMessage());
|
|
}
|
|
|
|
/**
|
|
* @param $fieldName
|
|
* @param $maximumLength
|
|
* @param $violationMessage
|
|
*/
|
|
protected function validateFieldLength($fieldName, $maximumLength, $violationMessage)
|
|
{
|
|
$emailField = $this->getField($fieldName);
|
|
if (Tools::strlen($emailField->getValue()) > $maximumLength) {
|
|
$emailField->addError($violationMessage);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return mixed
|
|
*/
|
|
protected function getEmailMaxLengthViolationMessage()
|
|
{
|
|
return $this->translator->trans(
|
|
'The %1$s field is too long (%2$d chars max).',
|
|
array('email', 255),
|
|
'Shop.Notifications.Error'
|
|
);
|
|
}
|
|
|
|
protected function getFirstNameMaxLengthViolationMessage()
|
|
{
|
|
return $this->translator->trans(
|
|
'The %1$s field is too long (%2$d chars max).',
|
|
array('first name', 255),
|
|
'Shop.Notifications.Error'
|
|
);
|
|
}
|
|
|
|
protected function getLastNameMaxLengthViolationMessage()
|
|
{
|
|
return $this->translator->trans(
|
|
'The %1$s field is too long (%2$d chars max).',
|
|
array('last name', 255),
|
|
'Shop.Notifications.Error'
|
|
);
|
|
}
|
|
|
|
|
|
public function submit($silentRegistration = false)
|
|
{
|
|
if ($this->context->customer->isLogged()) {
|
|
$this->formFields['password']->setRequired(false);
|
|
}
|
|
if ($this->validate($silentRegistration)) {
|
|
$clearTextPassword = ($silentRegistration) ? '' : $this->getValue('password');
|
|
// $newPassword is never used, we don't change password on checkout form
|
|
// password change is possible only through Prestashop's controllers
|
|
//$newPassword = $this->getValue('new_password');
|
|
|
|
// TheCheckout modification, to allow customer details (birthday, social title) modification
|
|
// after customer have been logged in.
|
|
if ($this->context->customer->isLogged()) {
|
|
$customer = $this->getCustomer();
|
|
$saveOk = $customer->save();
|
|
|
|
if ($saveOk) {
|
|
//$this->context->updateCustomer($customer);
|
|
//$this->_updateCustomerInContext($customer);
|
|
$this->context->cart->update();
|
|
Hook::exec('actionCustomerAccountUpdate', array(
|
|
'customer' => $customer,
|
|
));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
$ok = $this->customerPersister->saveCustomer(
|
|
$this->getCustomer(),
|
|
$clearTextPassword
|
|
);
|
|
|
|
// $ok = $this->customerPersister->save(
|
|
// $this->getCustomer(),
|
|
// $clearTextPassword,
|
|
// $newPassword
|
|
// );
|
|
|
|
if (!$ok) {
|
|
foreach ($this->customerPersister->getErrors() as $field => $errors) {
|
|
$this->formFields[$field]->setErrors($errors);
|
|
}
|
|
}
|
|
|
|
return $ok;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function getTemplateVariables()
|
|
{
|
|
return array(
|
|
'action' => $this->action,
|
|
'urls' => $this->urls,
|
|
'errors' => $this->getErrors(),
|
|
'hook_create_account_form' => Hook::exec('displayCustomerAccountForm'),
|
|
'formFields' => array_map(
|
|
function (FormField $field) {
|
|
return $field->toArray();
|
|
},
|
|
$this->formFields
|
|
),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* This function call the hook validateCustomerFormFields of every modules
|
|
* which added one or several fields to the customer registration form.
|
|
*
|
|
* Note: they won't get all the fields from the form, but only the one
|
|
* they added.
|
|
*/
|
|
private function validateByModules()
|
|
{
|
|
$formFieldsAssociated = array();
|
|
// Group FormField instances by module name
|
|
foreach ($this->formFields as $formField) {
|
|
if (!empty($formField->moduleName)) {
|
|
$formFieldsAssociated[$formField->moduleName][] = $formField;
|
|
}
|
|
}
|
|
// Because of security reasons (i.e password), we don't send all
|
|
// the values to the module but only the ones it created
|
|
foreach ($formFieldsAssociated as $moduleName => $formFields) {
|
|
if ($moduleId = Module::getModuleIdByName($moduleName)) {
|
|
$validatedCustomerFormFields = Hook::exec('validateCustomerFormFields', array('fields' => $formFields),
|
|
$moduleId, true);
|
|
|
|
if (is_array($validatedCustomerFormFields)) {
|
|
array_merge($this->formFields, $validatedCustomerFormFields);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Performs validation on field values.
|
|
* Adds error to the field object if value is not as expected.
|
|
*/
|
|
private function validateFieldsValues()
|
|
{
|
|
$this->validateFieldIsCustomerName('firstname');
|
|
$this->validateFieldIsCustomerName('lastname');
|
|
}
|
|
|
|
/**
|
|
* Checks whether a field's value is a valid customer(person) name.
|
|
*
|
|
* @param string $fieldName
|
|
*/
|
|
private function validateFieldIsCustomerName(string $fieldName)
|
|
{
|
|
$field = $this->getField($fieldName);
|
|
if (null === $field) {
|
|
return;
|
|
}
|
|
$value = $field->getValue();
|
|
if (!empty($value) && (method_exists('Validate', 'isCustomerName')) && false === (bool) Validate::isCustomerName($value)) {
|
|
$field->addError($this->translator->trans(
|
|
'Invalid format.',
|
|
[],
|
|
'Shop.Forms.Errors'
|
|
));
|
|
}
|
|
}
|
|
}
|