Initial commit

This commit is contained in:
2020-10-07 10:37:15 +02:00
commit ce5f440392
28157 changed files with 4429172 additions and 0 deletions

View File

@@ -0,0 +1,203 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* 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 https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use PrestaShop\PrestaShop\Core\Foundation\Templating\RenderableProxy;
use Symfony\Component\Translation\TranslatorInterface;
abstract class AbstractFormCore implements FormInterface
{
private $smarty;
protected $translator;
protected $constraintTranslator;
protected $action;
protected $template;
protected $formatter;
protected $formFields = [];
protected $errors = ['' => []];
public function __construct(
Smarty $smarty,
TranslatorInterface $translator,
FormFormatterInterface $formatter
) {
$this->smarty = $smarty;
$this->translator = $translator;
$this->formatter = $formatter;
$this->constraintTranslator = new ValidateConstraintTranslator(
$this->translator
);
}
public function getFormatter()
{
return $this->formatter;
}
public function setAction($action)
{
$this->action = $action;
return $this;
}
public function getAction()
{
return $this->action;
}
public function getErrors()
{
foreach ($this->formFields as $field) {
$this->errors[$field->getName()] = $field->getErrors();
}
return $this->errors;
}
public function hasErrors()
{
foreach ($this->getErrors() as $errors) {
if (!empty($errors)) {
return true;
}
}
return false;
}
abstract public function getTemplateVariables();
public function setTemplate($template)
{
$this->template = $template;
return $this;
}
public function getTemplate()
{
return $this->template;
}
public function render(array $extraVariables = [])
{
$scope = $this->smarty->createData(
$this->smarty
);
$scope->assign($extraVariables);
$scope->assign($this->getTemplateVariables());
$tpl = $this->smarty->createTemplate(
$this->getTemplate(),
$scope
);
return $tpl->fetch();
}
public function getProxy()
{
return new RenderableProxy($this);
}
public function validate()
{
foreach ($this->formFields as $field) {
if ($field->isRequired() && !$field->getValue()) {
$field->addError(
$this->constraintTranslator->translate('required')
);
continue;
} elseif (!$field->isRequired() && !$field->getValue()) {
continue;
}
foreach ($field->getConstraints() as $constraint) {
if (!Validate::$constraint($field->getValue())) {
$field->addError(
$this->constraintTranslator->translate($constraint)
);
}
}
}
return !$this->hasErrors();
}
public function fillWith(array $params = [])
{
$newFields = $this->formatter->getFormat();
foreach ($newFields as $field) {
if (array_key_exists($field->getName(), $this->formFields)) {
// keep current value if set
$field->setValue($this->formFields[$field->getName()]->getValue());
}
if (array_key_exists($field->getName(), $params)) {
// overwrite it if necessary
$field->setValue($params[$field->getName()]);
} elseif ($field->getType() === 'checkbox') {
// checkboxes that are not submitted
// are interpreted as booleans switched off
$field->setValue(false);
}
}
$this->formFields = $newFields;
return $this;
}
public function getField($field_name)
{
if (array_key_exists($field_name, $this->formFields)) {
return $this->formFields[$field_name];
}
return null;
}
public function getValue($field_name)
{
if ($field = $this->getField($field_name)) {
return $field->getValue();
}
return null;
}
public function setValue($field_name, $value)
{
$this->getField($field_name)->setValue($value);
return $this;
}
}

View File

@@ -0,0 +1,220 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* 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 https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use Symfony\Component\Translation\TranslatorInterface;
/**
* StarterTheme TODO: FIXME:
* In the old days, when updating an address, we actually:
* - checked if the address was used by an order
* - if so, just mark it as deleted and create a new one
* - otherwise, update it like a normal entity
* I *think* this is not necessary now because the invoicing thing
* does its own historization. But this should be checked more thoroughly.
*/
class CustomerAddressFormCore extends AbstractForm
{
private $language;
protected $template = 'customer/_partials/address-form.tpl';
private $address;
private $persister;
public function __construct(
Smarty $smarty,
Language $language,
TranslatorInterface $translator,
CustomerAddressPersister $persister,
CustomerAddressFormatter $formatter
) {
parent::__construct(
$smarty,
$translator,
$formatter
);
$this->language = $language;
$this->persister = $persister;
}
public function loadAddressById($id_address)
{
$context = Context::getContext();
$this->address = new Address($id_address, $this->language->id);
if ($this->address->id === null) {
return Tools::redirect('index.php?controller=404');
}
if (!$context->customer->isLogged() && !$context->customer->isGuest()) {
return Tools::redirect('/index.php?controller=authentication');
}
if ($this->address->id_customer != $context->customer->id) {
return Tools::redirect('index.php?controller=404');
}
$params = get_object_vars($this->address);
$params['id_address'] = $this->address->id;
return $this->fillWith($params);
}
public function fillWith(array $params = [])
{
// This form is very tricky: fields may change depending on which
// country is being submitted!
// So we first update the format if a new id_country was set.
if (isset($params['id_country'])
&& $params['id_country'] != $this->formatter->getCountry()->id
) {
$this->formatter->setCountry(new Country(
$params['id_country'],
$this->language->id
));
}
return parent::fillWith($params);
}
public function validate()
{
$is_valid = true;
if (($postcode = $this->getField('postcode'))) {
if ($postcode->isRequired()) {
$country = $this->formatter->getCountry();
if (!$country->checkZipCode($postcode->getValue())) {
$postcode->addError($this->translator->trans(
'Invalid postcode - should look like "%zipcode%"',
array('%zipcode%' => $country->zip_code_format),
'Shop.Forms.Errors'
));
$is_valid = false;
}
}
}
if (($hookReturn = Hook::exec('actionValidateCustomerAddressForm', array('form' => $this))) !== '') {
$is_valid &= (bool) $hookReturn;
}
return $is_valid && parent::validate();
}
public function submit()
{
if (!$this->validate()) {
return false;
}
$address = new Address(
Tools::getValue('id_address'),
$this->language->id
);
foreach ($this->formFields as $formField) {
$address->{$formField->getName()} = $formField->getValue();
}
if (!isset($this->formFields['id_state'])) {
$address->id_state = 0;
}
if (empty($address->alias)) {
$address->alias = $this->translator->trans('My Address', [], 'Shop.Theme.Checkout');
}
Hook::exec('actionSubmitCustomerAddressForm', array('address' => &$address));
$this->setAddress($address);
return $this->getPersister()->save(
$address,
$this->getValue('token')
);
}
/**
* @return Address
*/
public function getAddress()
{
return $this->address;
}
/**
* @return CustomerAddressPersister
*/
protected function getPersister()
{
return $this->persister;
}
protected function setAddress(Address $address)
{
$this->address = $address;
}
public function getTemplateVariables()
{
$context = Context::getContext();
if (!$this->formFields) {
// This is usually done by fillWith but the form may be
// rendered before fillWith is called.
// I don't want to assign formFields in the constructor
// because it accesses the DB and a constructor should not
// have side effects.
$this->formFields = $this->formatter->getFormat();
}
$this->setValue('token', $this->persister->getToken());
$formFields = array_map(
function (FormField $item) {
return $item->toArray();
},
$this->formFields
);
if (empty($formFields['firstname']['value'])) {
$formFields['firstname']['value'] = $context->customer->firstname;
}
if (empty($formFields['lastname']['value'])) {
$formFields['lastname']['value'] = $context->customer->lastname;
}
return array(
'id_address' => (isset($this->address->id)) ? $this->address->id : 0,
'action' => $this->action,
'errors' => $this->getErrors(),
'formFields' => $formFields,
);
}
}

View File

@@ -0,0 +1,238 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* 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 https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use Symfony\Component\Translation\TranslatorInterface;
class CustomerAddressFormatterCore implements FormFormatterInterface
{
private $country;
private $translator;
private $availableCountries;
private $definition;
public function __construct(
Country $country,
TranslatorInterface $translator,
array $availableCountries
) {
$this->country = $country;
$this->translator = $translator;
$this->availableCountries = $availableCountries;
$this->definition = Address::$definition['fields'];
}
public function setCountry(Country $country)
{
$this->country = $country;
return $this;
}
public function getCountry()
{
return $this->country;
}
public function getFormat()
{
$fields = AddressFormat::getOrderedAddressFields(
$this->country->id,
true,
true
);
$required = array_flip(AddressFormat::getFieldsRequired());
$format = [
'back' => (new FormField())
->setName('back')
->setType('hidden'),
'token' => (new FormField())
->setName('token')
->setType('hidden'),
'alias' => (new FormField())
->setName('alias')
->setLabel(
$this->getFieldLabel('alias')
),
];
foreach ($fields as $field) {
$formField = new FormField();
$formField->setName($field);
$fieldParts = explode(':', $field, 2);
if (count($fieldParts) === 1) {
if ($field === 'postcode') {
if ($this->country->need_zip_code) {
$formField->setRequired(true);
}
} elseif ($field === 'phone') {
$formField->setType('tel');
} elseif ($field === 'dni' && null !== $this->country) {
if ($this->country->need_identification_number) {
$formField->setRequired(true);
}
}
} elseif (count($fieldParts) === 2) {
list($entity, $entityField) = $fieldParts;
// Fields specified using the Entity:field
// notation are actually references to other
// entities, so they should be displayed as a select
$formField->setType('select');
// Also, what we really want is the id of the linked entity
$formField->setName('id_' . strtolower($entity));
if ($entity === 'Country') {
$formField->setType('countrySelect');
$formField->setValue($this->country->id);
foreach ($this->availableCountries as $country) {
$formField->addAvailableValue(
$country['id_country'],
$country[$entityField]
);
}
} elseif ($entity === 'State') {
if ($this->country->contains_states) {
$states = State::getStatesByIdCountry($this->country->id, true);
foreach ($states as $state) {
$formField->addAvailableValue(
$state['id_state'],
$state[$entityField]
);
}
$formField->setRequired(true);
}
}
}
$formField->setLabel($this->getFieldLabel($field));
if (!$formField->isRequired()) {
// Only trust the $required array for fields
// that are not marked as required.
// $required doesn't have all the info, and fields
// may be required for other reasons than what
// AddressFormat::getFieldsRequired() says.
$formField->setRequired(
array_key_exists($field, $required)
);
}
$format[$formField->getName()] = $formField;
}
//To add the extra fields in address form
$additionalAddressFormFields = Hook::exec('additionalCustomerAddressFields', array(), null, true);
if (is_array($additionalAddressFormFields)) {
foreach ($additionalAddressFormFields as $moduleName => $additionnalFormFields) {
if (!is_array($additionnalFormFields)) {
continue;
}
foreach ($additionnalFormFields as $formField) {
$formField->moduleName = $moduleName;
$format[$moduleName . '_' . $formField->getName()] = $formField;
}
}
}
return $this->addConstraints(
$this->addMaxLength(
$format
)
);
}
private function addConstraints(array $format)
{
foreach ($format as $field) {
if (!empty($this->definition[$field->getName()]['validate'])) {
$field->addConstraint(
$this->definition[$field->getName()]['validate']
);
}
}
return $format;
}
private function addMaxLength(array $format)
{
foreach ($format as $field) {
if (!empty($this->definition[$field->getName()]['size'])) {
$field->setMaxLength(
$this->definition[$field->getName()]['size']
);
}
}
return $format;
}
private function getFieldLabel($field)
{
// Country:name => Country, Country:iso_code => Country,
// same label regardless of which field is used for mapping.
$field = explode(':', $field)[0];
switch ($field) {
case 'alias':
return $this->translator->trans('Alias', [], 'Shop.Forms.Labels');
case 'firstname':
return $this->translator->trans('First name', [], 'Shop.Forms.Labels');
case 'lastname':
return $this->translator->trans('Last name', [], 'Shop.Forms.Labels');
case 'address1':
return $this->translator->trans('Address', [], 'Shop.Forms.Labels');
case 'address2':
return $this->translator->trans('Address Complement', [], 'Shop.Forms.Labels');
case 'postcode':
return $this->translator->trans('Zip/Postal Code', [], 'Shop.Forms.Labels');
case 'city':
return $this->translator->trans('City', [], 'Shop.Forms.Labels');
case 'Country':
return $this->translator->trans('Country', [], 'Shop.Forms.Labels');
case 'State':
return $this->translator->trans('State', [], 'Shop.Forms.Labels');
case 'phone':
return $this->translator->trans('Phone', [], 'Shop.Forms.Labels');
case 'phone_mobile':
return $this->translator->trans('Mobile phone', [], 'Shop.Forms.Labels');
case 'company':
return $this->translator->trans('Company', [], 'Shop.Forms.Labels');
case 'vat_number':
return $this->translator->trans('VAT number', [], 'Shop.Forms.Labels');
case 'dni':
return $this->translator->trans('Identification number', [], 'Shop.Forms.Labels');
case 'other':
return $this->translator->trans('Other', [], 'Shop.Forms.Labels');
default:
return $field;
}
}
}

View File

@@ -0,0 +1,101 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* 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 https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class CustomerAddressPersisterCore
{
private $customer;
private $token;
private $cart;
public function __construct(Customer $customer, Cart $cart, $token)
{
$this->customer = $customer;
$this->cart = $cart;
$this->token = $token;
}
public function getToken()
{
return $this->token;
}
private function authorizeChange(Address $address, $token)
{
if ($address->id_customer && (int) $address->id_customer !== (int) $this->customer->id) {
// Can't touch anybody else's address
return false;
}
if ($token !== $this->token) {
// XSS?
return false;
}
return true;
}
public function save(Address $address, $token)
{
if (!$this->authorizeChange($address, $token)) {
return false;
}
$address->id_customer = $this->customer->id;
if ($address->isUsed()) {
$old_address = new Address($address->id);
$address->id = $address->id_address = null;
return $address->save() && $old_address->delete();
}
return $address->save();
}
public function delete(Address $address, $token)
{
if (!$this->authorizeChange($address, $token)) {
return false;
}
$id = $address->id;
$ok = $address->delete();
if ($ok) {
if ($this->cart->id_address_invoice == $id) {
unset($this->cart->id_address_invoice);
}
if ($this->cart->id_address_delivery == $id) {
unset($this->cart->id_address_delivery);
$this->cart->updateAddressId(
$id,
Address::getFirstCustomerAddressId($this->customer->id)
);
}
}
return $ok;
}
}

View File

@@ -0,0 +1,252 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* 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 https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use Symfony\Component\Translation\TranslatorInterface;
/**
* StarterTheme TODO: B2B fields, Genders, CSRF.
*/
class CustomerFormCore extends AbstractForm
{
protected $template = 'customer/_partials/customer-form.tpl';
private $context;
private $urls;
private $customerPersister;
private $guest_allowed;
private $passwordRequired = true;
public function __construct(
Smarty $smarty,
Context $context,
TranslatorInterface $translator,
CustomerFormatter $formatter,
CustomerPersister $customerPersister,
array $urls
) {
parent::__construct(
$smarty,
$translator,
$formatter
);
$this->context = $context;
$this->urls = $urls;
$this->customerPersister = $customerPersister;
}
public function setGuestAllowed($guest_allowed = true)
{
$this->formatter->setPasswordRequired(!$guest_allowed);
$this->guest_allowed = $guest_allowed;
return $this;
}
public function setPasswordRequired($passwordRequired)
{
$this->passwordRequired = $passwordRequired;
return $this;
}
public function fillFromCustomer(Customer $customer)
{
$params = get_object_vars($customer);
$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->context->customer->id);
foreach ($this->formFields as $field) {
$customerField = $field->getName();
if (property_exists($customer, $customerField)) {
$customer->$customerField = $field->getValue();
}
}
return $customer;
}
public function validate()
{
$emailField = $this->getField('email');
$id_customer = Customer::customerExists($emailField->getValue(), true, true);
$customer = $this->getCustomer();
if ($id_customer && $id_customer != $customer->id) {
$emailField->addError($this->translator->trans(
'The email is already used, please choose another one or sign in',
array(),
'Shop.Notifications.Error'
));
}
// check birthdayField against null case is mandatory.
$birthdayField = $this->getField('birthday');
if (!empty($birthdayField) &&
!empty($birthdayField->getValue()) &&
Validate::isBirthDate($birthdayField->getValue(), $this->context->language->date_format_lite)
) {
$dateBuilt = DateTime::createFromFormat(
$this->context->language->date_format_lite,
$birthdayField->getValue()
);
$birthdayField->setValue($dateBuilt->format('Y-m-d'));
}
$this->validateFieldsLengths();
$this->validateByModules();
return parent::validate();
}
protected function validateFieldsLengths()
{
$this->validateFieldLength('email', 255, $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 (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()
{
if ($this->validate()) {
$clearTextPassword = $this->getValue('password');
$newPassword = $this->getValue('new_password');
$ok = $this->customerPersister->save(
$this->getCustomer(),
$clearTextPassword,
$newPassword,
$this->passwordRequired
);
if (!$ok) {
foreach ($this->customerPersister->getErrors() as $field => $errors) {
$this->formFields[$field]->setErrors($errors);
}
}
return $ok;
}
return false;
}
public function getTemplateVariables()
{
return [
'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)) {
// ToDo : replace Hook::exec with HookFinder, because we expect a specific class here
$validatedCustomerFormFields = Hook::exec('validateCustomerFormFields', array('fields' => $formFields), $moduleId, true);
if (is_array($validatedCustomerFormFields)) {
array_merge($this->formFields, $validatedCustomerFormFields);
}
}
}
}
}

View File

@@ -0,0 +1,260 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* 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 https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use Symfony\Component\Translation\TranslatorInterface;
class CustomerFormatterCore implements FormFormatterInterface
{
private $translator;
private $language;
private $ask_for_birthdate = true;
private $ask_for_partner_optin = true;
private $partner_optin_is_required = true;
private $ask_for_password = true;
private $password_is_required = true;
private $ask_for_new_password = false;
public function __construct(
TranslatorInterface $translator,
Language $language
) {
$this->translator = $translator;
$this->language = $language;
}
public function setAskForBirthdate($ask_for_birthdate)
{
$this->ask_for_birthdate = $ask_for_birthdate;
return $this;
}
public function setAskForPartnerOptin($ask_for_partner_optin)
{
$this->ask_for_partner_optin = $ask_for_partner_optin;
return $this;
}
public function setPartnerOptinRequired($partner_optin_is_required)
{
$this->partner_optin_is_required = $partner_optin_is_required;
return $this;
}
public function setAskForPassword($ask_for_password)
{
$this->ask_for_password = $ask_for_password;
return $this;
}
public function setAskForNewPassword($ask_for_new_password)
{
$this->ask_for_new_password = $ask_for_new_password;
return $this;
}
public function setPasswordRequired($password_is_required)
{
$this->password_is_required = $password_is_required;
return $this;
}
public function getFormat()
{
$format = [];
$genders = Gender::getGenders($this->language->id);
if ($genders->count() > 0) {
$genderField = (new FormField())
->setName('id_gender')
->setType('radio-buttons')
->setLabel(
$this->translator->trans(
'Social title',
[],
'Shop.Forms.Labels'
)
);
foreach ($genders as $gender) {
$genderField->addAvailableValue($gender->id, $gender->name);
}
$format[$genderField->getName()] = $genderField;
}
$format['firstname'] = (new FormField())
->setName('firstname')
->setLabel(
$this->translator->trans(
'First name',
[],
'Shop.Forms.Labels'
)
)
->setRequired(true);
$format['lastname'] = (new FormField())
->setName('lastname')
->setLabel(
$this->translator->trans(
'Last name',
[],
'Shop.Forms.Labels'
)
)
->setRequired(true);
if (Configuration::get('PS_B2B_ENABLE')) {
$format['company'] = (new FormField())
->setName('company')
->setType('text')
->setLabel($this->translator->trans(
'Company',
[],
'Shop.Forms.Labels'
));
$format['siret'] = (new FormField())
->setName('siret')
->setType('text')
->setLabel($this->translator->trans(
// Please localize this string with the applicable registration number type in your country. For example : "SIRET" in France and "Código fiscal" in Spain.
'Identification number',
[],
'Shop.Forms.Labels'
));
}
$format['email'] = (new FormField())
->setName('email')
->setType('email')
->setLabel(
$this->translator->trans(
'Email',
[],
'Shop.Forms.Labels'
)
)
->setRequired(true);
if ($this->ask_for_password) {
$format['password'] = (new FormField())
->setName('password')
->setType('password')
->setLabel(
$this->translator->trans(
'Password',
[],
'Shop.Forms.Labels'
)
)
->setRequired($this->password_is_required);
}
if ($this->ask_for_new_password) {
$format['new_password'] = (new FormField())
->setName('new_password')
->setType('password')
->setLabel(
$this->translator->trans(
'New password',
[],
'Shop.Forms.Labels'
)
);
}
if ($this->ask_for_birthdate) {
$format['birthday'] = (new FormField())
->setName('birthday')
->setType('text')
->setLabel(
$this->translator->trans(
'Birthdate',
[],
'Shop.Forms.Labels'
)
)
->addAvailableValue('placeholder', Tools::getDateFormat())
->addAvailableValue(
'comment',
$this->translator->trans('(E.g.: %date_format%)', array('%date_format%' => Tools::formatDateStr('31 May 1970')), 'Shop.Forms.Help')
);
}
if ($this->ask_for_partner_optin) {
$format['optin'] = (new FormField())
->setName('optin')
->setType('checkbox')
->setLabel(
$this->translator->trans(
'Receive offers from our partners',
[],
'Shop.Theme.Customeraccount'
)
)
->setRequired($this->partner_optin_is_required);
}
// ToDo, replace the hook exec with HookFinder when the associated PR will be merged
$additionalCustomerFormFields = Hook::exec('additionalCustomerFormFields', array(), null, true);
if (is_array($additionalCustomerFormFields)) {
foreach ($additionalCustomerFormFields as $moduleName => $additionnalFormFields) {
if (!is_array($additionnalFormFields)) {
continue;
}
foreach ($additionnalFormFields as $formField) {
$formField->moduleName = $moduleName;
$format[$moduleName . '_' . $formField->getName()] = $formField;
}
}
}
// TODO: TVA etc.?
return $this->addConstraints($format);
}
private function addConstraints(array $format)
{
$constraints = Customer::$definition['fields'];
foreach ($format as $field) {
if (!empty($constraints[$field->getName()]['validate'])) {
$field->addConstraint(
$constraints[$field->getName()]['validate']
);
}
}
return $format;
}
}

View File

@@ -0,0 +1,104 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* 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 https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use Symfony\Component\Translation\TranslatorInterface;
class CustomerLoginFormCore extends AbstractForm
{
private $context;
private $urls;
protected $template = 'customer/_partials/login-form.tpl';
public function __construct(
Smarty $smarty,
Context $context,
TranslatorInterface $translator,
CustomerLoginFormatter $formatter,
array $urls
) {
parent::__construct(
$smarty,
$translator,
$formatter
);
$this->context = $context;
$this->translator = $translator;
$this->formatter = $formatter;
$this->urls = $urls;
$this->constraintTranslator = new ValidateConstraintTranslator(
$this->translator
);
}
public function submit()
{
if ($this->validate()) {
Hook::exec('actionAuthenticationBefore');
$customer = new Customer();
$authentication = $customer->getByEmail(
$this->getValue('email'),
$this->getValue('password')
);
if (isset($authentication->active) && !$authentication->active) {
$this->errors[''][] = $this->translator->trans('Your account isn\'t available at this time, please contact us', [], 'Shop.Notifications.Error');
} elseif (!$authentication || !$customer->id || $customer->is_guest) {
$this->errors[''][] = $this->translator->trans('Authentication failed.', [], 'Shop.Notifications.Error');
} else {
$this->context->updateCustomer($customer);
Hook::exec('actionAuthentication', ['customer' => $this->context->customer]);
// Login information have changed, so we check if the cart rules still apply
CartRule::autoRemoveFromCart($this->context);
CartRule::autoAddToCart($this->context);
}
}
return !$this->hasErrors();
}
public function getTemplateVariables()
{
if (!$this->formFields) {
$this->formFields = $this->formatter->getFormat();
}
return [
'action' => $this->action,
'urls' => $this->urls,
'formFields' => array_map(
function (FormField $field) {
return $field->toArray();
},
$this->formFields
),
'errors' => $this->getErrors(),
];
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* 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 https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use Symfony\Component\Translation\TranslatorInterface;
class CustomerLoginFormatterCore implements FormFormatterInterface
{
private $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function getFormat()
{
return [
'back' => (new FormField())
->setName('back')
->setType('hidden'),
'email' => (new FormField())
->setName('email')
->setType('email')
->setRequired(true)
->setLabel($this->translator->trans(
'Email',
[],
'Shop.Forms.Labels'
))
->addConstraint('isEmail'),
'password' => (new FormField())
->setName('password')
->setType('password')
->setRequired(true)
->setLabel($this->translator->trans(
'Password',
[],
'Shop.Forms.Labels'
))
->addConstraint('isPasswd'),
];
}
}

View File

@@ -0,0 +1,232 @@
<?php
/**
* 2007-2020 PrestaShop and Contributors
*
* 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 https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use PrestaShop\PrestaShop\Core\Crypto\Hashing as Crypto;
use Symfony\Component\Translation\TranslatorInterface;
class CustomerPersisterCore
{
private $errors = [];
private $context;
private $crypto;
private $translator;
private $guest_allowed;
public function __construct(
Context $context,
Crypto $crypto,
TranslatorInterface $translator,
$guest_allowed
) {
$this->context = $context;
$this->crypto = $crypto;
$this->translator = $translator;
$this->guest_allowed = $guest_allowed;
}
public function getErrors()
{
return $this->errors;
}
public function save(Customer $customer, $clearTextPassword, $newPassword = '', $passwordRequired = true)
{
if ($customer->id) {
return $this->update($customer, $clearTextPassword, $newPassword, $passwordRequired);
}
return $this->create($customer, $clearTextPassword);
}
private function update(Customer $customer, $clearTextPassword, $newPassword, $passwordRequired = true)
{
if (!$customer->is_guest && $passwordRequired && !$this->crypto->checkHash(
$clearTextPassword,
$customer->passwd,
_COOKIE_KEY_
)) {
$msg = $this->translator->trans(
'Invalid email/password combination',
[],
'Shop.Notifications.Error'
);
$this->errors['email'][] = $msg;
$this->errors['password'][] = $msg;
return false;
}
if (!$customer->is_guest) {
$customer->passwd = $this->crypto->hash(
$newPassword ? $newPassword : $clearTextPassword,
_COOKIE_KEY_
);
}
if ($customer->is_guest || !$passwordRequired) {
// TODO SECURITY: Audit requested
if ($customer->id != $this->context->customer->id) {
// Since we're updating a customer without
// checking the password, we need to check that
// the customer being updated is the one from the
// current session.
// The error message is not great,
// but it should only be displayed to hackers
// so it should not be an issue :)
$this->errors['email'][] = $this->translator->trans(
'There seems to be an issue with your account, please contact support',
[],
'Shop.Notifications.Error'
);
return false;
}
}
$guest_to_customer = false;
if ($clearTextPassword && $customer->is_guest) {
$guest_to_customer = true;
$customer->is_guest = false;
$customer->passwd = $this->crypto->hash(
$clearTextPassword,
_COOKIE_KEY_
);
}
if ($customer->is_guest || $guest_to_customer) {
// guest cannot update their email to that of an existing real customer
if (Customer::customerExists($customer->email, false, true)) {
$this->errors['email'][] = $this->translator->trans(
'An account was already registered with this email address',
[],
'Shop.Notifications.Error'
);
return false;
}
}
if ($customer->email != $this->context->customer->email) {
$customer->removeResetPasswordToken();
}
$ok = $customer->save();
if ($ok) {
$this->context->updateCustomer($customer);
$this->context->cart->update();
Hook::exec('actionCustomerAccountUpdate', [
'customer' => $customer,
]);
if ($guest_to_customer) {
$this->sendConfirmationMail($customer);
}
}
return $ok;
}
private function create(Customer $customer, $clearTextPassword)
{
if (!$clearTextPassword) {
if (!$this->guest_allowed) {
$this->errors['password'][] = $this->translator->trans(
'Password is required',
[],
'Shop.Notifications.Error'
);
return false;
}
/**
* Warning: this is only safe provided
* that guests cannot log in even with the generated
* password. That's the case at least at the time of writing.
*/
$clearTextPassword = $this->crypto->hash(
microtime(),
_COOKIE_KEY_
);
$customer->is_guest = true;
}
$customer->passwd = $this->crypto->hash(
$clearTextPassword,
_COOKIE_KEY_
);
if (Customer::customerExists($customer->email, false, true)) {
$this->errors['email'][] = $this->translator->trans(
'An account was already registered with this email address',
[],
'Shop.Notifications.Error'
);
return false;
}
$ok = $customer->save();
if ($ok) {
$this->context->updateCustomer($customer);
$this->context->cart->update();
$this->sendConfirmationMail($customer);
Hook::exec('actionCustomerAccountAdd', array(
'newCustomer' => $customer,
));
}
return $ok;
}
private function sendConfirmationMail(Customer $customer)
{
if ($customer->is_guest || !Configuration::get('PS_CUSTOMER_CREATION_EMAIL')) {
return true;
}
return Mail::Send(
$this->context->language->id,
'account',
$this->translator->trans(
'Welcome!',
array(),
'Emails.Subject'
),
array(
'{firstname}' => $customer->firstname,
'{lastname}' => $customer->lastname,
'{email}' => $customer->email,
),
$customer->email,
$customer->firstname . ' ' . $customer->lastname
);
}
}

184
classes/form/FormField.php Normal file
View File

@@ -0,0 +1,184 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* 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 https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class FormFieldCore
{
private $name = '';
private $type = 'text';
private $required = false;
private $label = '';
private $value = null;
private $availableValues = [];
private $maxLength = null;
private $errors = [];
private $constraints = [];
public function toArray()
{
return [
'name' => $this->getName(),
'type' => $this->getType(),
'required' => $this->isRequired(),
'label' => $this->getLabel(),
'value' => $this->getValue(),
'availableValues' => $this->getAvailableValues(),
'maxLength' => $this->getMaxLength(),
'errors' => $this->getErrors(),
];
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function getName()
{
return $this->name;
}
public function setType($type)
{
$this->type = $type;
return $this;
}
public function getType()
{
return $this->type;
}
public function setRequired($required)
{
$this->required = $required;
return $this;
}
public function isRequired()
{
return $this->required;
}
public function setLabel($label)
{
$this->label = $label;
return $this;
}
public function getLabel()
{
return $this->label;
}
public function setValue($value)
{
$this->value = $value;
return $this;
}
public function getValue()
{
return $this->value;
}
public function setAvailableValues(array $availableValues)
{
$this->availableValues = $availableValues;
return $this;
}
public function getAvailableValues()
{
return $this->availableValues;
}
public function addAvailableValue($availableValue, $label = null)
{
if (!$label) {
$label = $availableValue;
}
$this->availableValues[$availableValue] = $label;
return $this;
}
public function setMaxLength($max)
{
$this->maxLength = (int) $max;
return $this;
}
public function getMaxLength()
{
return $this->maxLength;
}
public function setErrors(array $errors)
{
$this->errors = $errors;
return $this;
}
public function getErrors()
{
return $this->errors;
}
public function addError($errorString)
{
$this->errors[] = $errorString;
return $this;
}
public function setConstraints(array $constraints)
{
$this->constraints = $constraints;
return $this;
}
public function addConstraint($constraint)
{
$this->constraints[] = $constraint;
return $this;
}
public function getConstraints()
{
return $this->constraints;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* 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 https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
interface FormFormatterInterface
{
public function getFormat();
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* 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 https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use PrestaShop\PrestaShop\Core\Foundation\Templating\RenderableInterface;
interface FormInterface extends RenderableInterface
{
public function setAction($action);
public function fillWith(array $params = []);
public function submit();
public function getErrors();
public function hasErrors();
public function render(array $extraVariables = []);
public function setTemplate($template);
}