[11/06/2024] Les premières modifs + installation de quelques modules indispensables

This commit is contained in:
2024-06-11 14:57:59 +02:00
parent 5ac5653ae5
commit 77cf2c7cc6
1626 changed files with 171457 additions and 131 deletions

View File

@@ -0,0 +1,257 @@
<?php
/*
* This file is part of the Thelia package.
* http://www.thelia.net
*
* (c) OpenStudio <info@thelia.net>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Front\Controller;
use Front\Front;
use Symfony\Component\Form\Form;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Thelia\Controller\Front\BaseFrontController;
use Thelia\Core\Event\Address\AddressCreateOrUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Form\Definition\FrontForm;
use Thelia\Form\Exception\FormValidationException;
use Thelia\Log\Tlog;
use Thelia\Model\AddressQuery;
use Thelia\Model\Customer;
use Thelia\Model\Event\AddressEvent;
/**
* Class AddressController.
*
* @author Manuel Raynaud <manu@raynaud.io>
*/
class AddressController extends BaseFrontController
{
/**
* Controller for generate modal containing update form
* Check if request is a XmlHttpRequest and address owner is the current customer.
*/
public function generateModalAction($address_id): void
{
$this->checkAuth();
$this->checkXmlHttpRequest();
}
/**
* Create controller.
* Check if customer is logged in.
*
* Dispatch TheliaEvents::ADDRESS_CREATE event
*/
public function createAction(EventDispatcherInterface $eventDispatcher)
{
$this->checkAuth();
$addressCreate = $this->createForm(FrontForm::ADDRESS_CREATE);
try {
/** @var Customer $customer */
$customer = $this->getSecurityContext()->getCustomerUser();
$form = $this->validateForm($addressCreate, 'post');
$event = $this->createAddressEvent($form);
$event->setCustomer($customer);
$eventDispatcher->dispatch($event, TheliaEvents::ADDRESS_CREATE);
return $this->generateSuccessRedirect($addressCreate);
} catch (FormValidationException $e) {
$message = $this->getTranslator()->trans('Please check your input: %s', ['%s' => $e->getMessage()], Front::MESSAGE_DOMAIN);
} catch (\Exception $e) {
$message = $this->getTranslator()->trans('Sorry, an error occured: %s', ['%s' => $e->getMessage()], Front::MESSAGE_DOMAIN);
}
Tlog::getInstance()->error(sprintf('Error during address creation process : %s', $message));
$addressCreate->setErrorMessage($message);
$this->getParserContext()
->addForm($addressCreate)
->setGeneralError($message)
;
// Redirect to error URL if defined
if ($addressCreate->hasErrorUrl()) {
return $this->generateErrorRedirect($addressCreate);
}
}
protected function createAddressEvent(Form $form)
{
return new AddressCreateOrUpdateEvent(
$form->get('label')->getData(),
$form->get('title')->getData(),
$form->get('firstname')->getData(),
$form->get('lastname')->getData(),
$form->get('address1')->getData(),
$form->get('address2')->getData(),
$form->get('address3')->getData(),
$form->get('zipcode')->getData(),
$form->get('city')->getData(),
$form->get('country')->getData(),
$form->get('cellphone')->getData(),
$form->get('phone')->getData(),
$form->get('company')->getData(),
$form->get('is_default')->getData(),
$form->get('state')->getData()
);
}
public function updateViewAction($address_id)
{
$this->checkAuth();
$customer = $this->getSecurityContext()->getCustomerUser();
$address = AddressQuery::create()->findPk($address_id);
if (!$address || $customer->getId() != $address->getCustomerId()) {
return $this->generateRedirectFromRoute('default');
}
$this->getParserContext()->set('address_id', $address_id);
}
public function processUpdateAction($address_id, EventDispatcherInterface $eventDispatcher)
{
$this->checkAuth();
$addressUpdate = $this->createForm(FrontForm::ADDRESS_UPDATE);
try {
$customer = $this->getSecurityContext()->getCustomerUser();
$form = $this->validateForm($addressUpdate);
$address = AddressQuery::create()->findPk($address_id);
if (null === $address) {
return $this->generateRedirectFromRoute('default');
}
if ($address->getCustomer()->getId() != $customer->getId()) {
return $this->generateRedirectFromRoute('default');
}
$event = $this->createAddressEvent($form);
$event->setAddress($address);
$eventDispatcher->dispatch($event, TheliaEvents::ADDRESS_UPDATE);
return $this->generateSuccessRedirect($addressUpdate);
} catch (FormValidationException $e) {
$message = $this->getTranslator()->trans('Please check your input: %s', ['%s' => $e->getMessage()], Front::MESSAGE_DOMAIN);
} catch (\Exception $e) {
$message = $this->getTranslator()->trans('Sorry, an error occured: %s', ['%s' => $e->getMessage()], Front::MESSAGE_DOMAIN);
}
$this->getParserContext()->set('address_id', $address_id);
Tlog::getInstance()->error(sprintf('Error during address creation process : %s', $message));
$addressUpdate->setErrorMessage($message);
$this->getParserContext()
->addForm($addressUpdate)
->setGeneralError($message)
;
if ($addressUpdate->hasErrorUrl()) {
return $this->generateErrorRedirect($addressUpdate);
}
}
public function deleteAction(EventDispatcherInterface $eventDispatcher, $address_id)
{
$this->checkAuth();
$error_message = false;
$customer = $this->getSecurityContext()->getCustomerUser();
$address = AddressQuery::create()->findPk($address_id);
if (!$address || $customer->getId() != $address->getCustomerId()) {
// If Ajax Request
if ($this->getRequest()->isXmlHttpRequest()) {
return $this->jsonResponse(
json_encode(
[
'success' => false,
'message' => $this->getTranslator()->trans(
'Error during address deletion process',
[],
Front::MESSAGE_DOMAIN
),
]
)
);
}
return $this->generateRedirectFromRoute('default');
}
try {
$eventDispatcher->dispatch(new AddressEvent($address), TheliaEvents::ADDRESS_DELETE);
} catch (\Exception $e) {
$error_message = $e->getMessage();
}
Tlog::getInstance()->error(sprintf('Error during address deletion : %s', $error_message));
// If Ajax Request
if ($this->getRequest()->isXmlHttpRequest()) {
if ($error_message) {
$response = $this->jsonResponse(json_encode([
'success' => false,
'message' => $error_message,
]));
} else {
$response = $this->jsonResponse(
json_encode([
'success' => true,
'message' => '',
])
);
}
return $response;
}
return $this->generateRedirectFromRoute('default', ['view' => 'account']);
}
public function makeAddressDefaultAction(EventDispatcherInterface $eventDispatcher, $addressId)
{
$this->checkAuth();
$address = AddressQuery::create()
->filterByCustomerId($this->getSecurityContext()->getCustomerUser()->getId())
->findPk($addressId)
;
if (null === $address) {
$this->pageNotFound();
}
try {
$event = new AddressEvent($address);
$eventDispatcher->dispatch($event, TheliaEvents::ADDRESS_DEFAULT);
} catch (\Exception $e) {
$this->getParserContext()
->setGeneralError($e->getMessage())
;
return $this->render('account');
}
return $this->generateRedirectFromRoute('default', ['view' => 'account']);
}
}

View File

@@ -0,0 +1,241 @@
<?php
/*
* This file is part of the Thelia package.
* http://www.thelia.net
*
* (c) OpenStudio <info@thelia.net>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Front\Controller;
use Front\Front;
use Propel\Runtime\Exception\PropelException;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Thelia\Controller\Front\BaseFrontController;
use Thelia\Core\Event\Cart\CartEvent;
use Thelia\Core\Event\Delivery\DeliveryPostageEvent;
use Thelia\Core\Event\Order\OrderEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Form\CartAdd;
use Thelia\Form\Definition\FrontForm;
use Thelia\Form\Exception\FormValidationException;
use Thelia\Log\Tlog;
use Thelia\Model\AddressQuery;
use Thelia\Model\ConfigQuery;
use Thelia\Tools\URL;
class CartController extends BaseFrontController
{
public function addItem(EventDispatcherInterface $eventDispatcher)
{
$request = $this->getRequest();
$cartAdd = $this->getAddCartForm($request);
$message = null;
try {
$form = $this->validateForm($cartAdd);
$cartEvent = $this->getCartEvent($eventDispatcher);
$cartEvent->bindForm($form);
$eventDispatcher->dispatch($cartEvent, TheliaEvents::CART_ADDITEM);
$this->afterModifyCart($eventDispatcher);
if (!$this->changeViewForAjax()) {
if (null !== $response = $this->generateSuccessRedirect($cartAdd)) {
return $response;
}
}
} catch (PropelException $e) {
Tlog::getInstance()->error(sprintf('Failed to add item to cart with message : %s', $e->getMessage()));
$message = $this->getTranslator()->trans(
'Failed to add this article to your cart, please try again',
[],
Front::MESSAGE_DOMAIN
);
} catch (FormValidationException $e) {
$message = $e->getMessage();
}
if ($message) {
$cartAdd->setErrorMessage($message);
$this->getParserContext()->addForm($cartAdd);
}
}
public function changeItem(EventDispatcherInterface $eventDispatcher)
{
$cartEvent = $this->getCartEvent($eventDispatcher);
$cartEvent->setCartItemId($this->getRequest()->get('cart_item'));
$cartEvent->setQuantity($this->getRequest()->get('quantity'));
try {
$this->getTokenProvider()->checkToken(
$this->getRequest()->query->get('_token')
);
$eventDispatcher->dispatch($cartEvent, TheliaEvents::CART_UPDATEITEM);
$this->afterModifyCart($eventDispatcher);
if (!$this->changeViewForAjax()) {
if (null !== $successUrl = $this->getRequest()->get('success_url')) {
return $this->generateRedirect(URL::getInstance()->absoluteUrl($successUrl));
}
}
} catch (\Exception $e) {
Tlog::getInstance()->error(sprintf('Failed to change cart item quantity: %s', $e->getMessage()));
$this->getParserContext()->setGeneralError($e->getMessage());
}
}
public function deleteItem(EventDispatcherInterface $eventDispatcher)
{
$cartEvent = $this->getCartEvent($eventDispatcher);
$cartEvent->setCartItemId($this->getRequest()->get('cart_item'));
try {
$this->getTokenProvider()->checkToken(
$this->getRequest()->query->get('_token')
);
$eventDispatcher->dispatch($cartEvent, TheliaEvents::CART_DELETEITEM);
$this->afterModifyCart($eventDispatcher);
} catch (\Exception $e) {
Tlog::getInstance()->error(sprintf('error during deleting cartItem with message : %s', $e->getMessage()));
$this->getParserContext()->setGeneralError($e->getMessage());
}
if (!$this->changeViewForAjax()) {
if (null !== $successUrl = $this->getRequest()->get('success_url')) {
return $this->generateRedirect(URL::getInstance()->absoluteUrl($successUrl));
}
}
}
protected function changeViewForAjax()
{
// If this is an ajax request, and if the template allow us to return an ajax result
if ($this->getRequest()->isXmlHttpRequest() && (0 === (int) $this->getRequest()->get('no_ajax_check', 0))) {
$request = $this->getRequest();
$view = $request->get('ajax-view', 'includes/mini-cart');
$request->attributes->set('_view', $view);
return true;
}
return false;
}
public function changeCountry()
{
$redirectUrl = URL::getInstance()->absoluteUrl('/cart');
$deliveryId = $this->getRequest()->get('country');
$cookieName = ConfigQuery::read('front_cart_country_cookie_name', 'fcccn');
$cookieExpires = ConfigQuery::read('front_cart_country_cookie_expires', 2592000);
$cookieExpires = (int) $cookieExpires ?: 2592000;
$cookie = new Cookie($cookieName, $deliveryId, time() + $cookieExpires, '/');
$response = $this->generateRedirect($redirectUrl);
$response->headers->setCookie($cookie);
return $response;
}
/**
* @return \Thelia\Core\Event\Cart\CartEvent
*/
protected function getCartEvent(EventDispatcherInterface $eventDispatcher)
{
$cart = $this->getSession()->getSessionCart($eventDispatcher);
return new CartEvent($cart);
}
/**
* Find the good way to construct the cart form.
*
* @return CartAdd
*/
private function getAddCartForm(Request $request)
{
/* @var CartAdd $cartAdd */
if ($request->isMethod('post')) {
$cartAdd = $this->createForm(FrontForm::CART_ADD);
} else {
$cartAdd = $this->createForm(
FrontForm::CART_ADD,
FormType::class,
[],
[
'csrf_protection' => false,
]
);
}
return $cartAdd;
}
/**
* @throws PropelException
*/
protected function afterModifyCart(EventDispatcherInterface $eventDispatcher): void
{
/* recalculate postage amount */
$order = $this->getSession()->getOrder();
if (null !== $order) {
$deliveryModule = $order->getModuleRelatedByDeliveryModuleId();
$deliveryAddress = AddressQuery::create()->findPk($order->getChoosenDeliveryAddress());
if (null !== $deliveryModule && null !== $deliveryAddress) {
$moduleInstance = $deliveryModule->getDeliveryModuleInstance($this->container);
$orderEvent = new OrderEvent($order);
try {
$deliveryPostageEvent = new DeliveryPostageEvent(
$moduleInstance,
$this->getSession()->getSessionCart($eventDispatcher),
$deliveryAddress
);
$eventDispatcher->dispatch(
$deliveryPostageEvent,
TheliaEvents::MODULE_DELIVERY_GET_POSTAGE,
);
$postage = $deliveryPostageEvent->getPostage();
if (null !== $postage) {
$orderEvent->setPostage($postage->getAmount());
$orderEvent->setPostageTax($postage->getAmountTax());
$orderEvent->setPostageTaxRuleTitle($postage->getTaxRuleTitle());
}
$eventDispatcher->dispatch($orderEvent, TheliaEvents::ORDER_SET_POSTAGE);
} catch (\Exception $ex) {
// The postage has been chosen, but changes in the cart causes an exception.
// Reset the postage data in the order
$orderEvent->setDeliveryModule(0);
$eventDispatcher->dispatch($orderEvent, TheliaEvents::ORDER_SET_DELIVERY_MODULE);
}
}
}
}
}

View File

@@ -0,0 +1,91 @@
<?php
/*
* This file is part of the Thelia package.
* http://www.thelia.net
*
* (c) OpenStudio <info@thelia.net>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Front\Controller;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Thelia\Controller\Front\BaseFrontController;
use Thelia\Core\Event\Contact\ContactEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Template\ParserContext;
use Thelia\Core\Translation\Translator;
use Thelia\Form\Definition\FrontForm;
use Thelia\Form\Exception\FormValidationException;
use Thelia\Log\Tlog;
use Thelia\Mailer\MailerFactory;
use Thelia\Model\ConfigQuery;
/**
* Class ContactController.
*
* @author Manuel Raynaud <manu@raynaud.io>
* @author Loïc Mo <lmo@openstudio.fr>
*/
class ContactController extends BaseFrontController
{
/**
* Send contact message.
*/
public function sendAction(EventDispatcherInterface $eventDispatcher, MailerFactory $mailer, ParserContext $parserContext)
{
$translator = Translator::getInstance();
$contactForm = $this->createForm(FrontForm::CONTACT);
try {
$form = $this->validateForm($contactForm);
$event = new ContactEvent($form);
$eventDispatcher->dispatch($event, TheliaEvents::CONTACT_SUBMIT);
$name = $translator?->trans('Sender name: %name%', ['%name%' => $event->getName()]);
$email = $translator?->trans('Sender\'s e-mail address: %email%', ['%email%' => $event->getEmail()]);
$message = $translator?->trans('Message content: %message%', ['%message%' => $event->getMessage()]);
$messageContent =
"<p>$name</p>\n".
"<p>$email</p>\n".
"<p>$message</p>";
$mailer->sendSimpleEmailMessage(
[ConfigQuery::getStoreEmail() => $event->getName()],
[ConfigQuery::getStoreEmail() => ConfigQuery::getStoreName()],
$event->getSubject(),
$messageContent,
strip_tags($messageContent),
[],
[],
[$event->getEmail() => $event->getName()]
);
if ($contactForm->hasSuccessUrl()) {
return $this->generateSuccessRedirect($contactForm);
}
return $this->generateRedirectFromRoute('contact.success');
} catch (FormValidationException $e) {
$error_message = $e->getMessage();
}
Tlog::getInstance()->error(sprintf('Error during sending contact mail : %s', $error_message));
$contactForm->setErrorMessage($error_message);
$parserContext
->addForm($contactForm)
->setGeneralError($error_message)
;
// Redirect to error URL if defined
if ($contactForm->hasErrorUrl()) {
return $this->generateErrorRedirect($contactForm);
}
}
}

View File

@@ -0,0 +1,158 @@
<?php
/*
* This file is part of the Thelia package.
* http://www.thelia.net
*
* (c) OpenStudio <info@thelia.net>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Front\Controller;
use Front\Front;
use Propel\Runtime\Exception\PropelException;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Thelia\Controller\Front\BaseFrontController;
use Thelia\Core\Event\Coupon\CouponConsumeEvent;
use Thelia\Core\Event\DefaultActionEvent;
use Thelia\Core\Event\Delivery\DeliveryPostageEvent;
use Thelia\Core\Event\Order\OrderEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Exception\UnmatchableConditionException;
use Thelia\Form\Definition\FrontForm;
use Thelia\Form\Exception\FormValidationException;
use Thelia\Log\Tlog;
use Thelia\Model\AddressQuery;
/**
* Class CouponController.
*
* @author Guillaume MOREL <gmorel@openstudio.fr>
*/
class CouponController extends BaseFrontController
{
/**
* Clear all coupons.
*/
public function clearAllCouponsAction(EventDispatcherInterface $eventDispatcher): void
{
// Dispatch Event to the Action
$eventDispatcher->dispatch(new DefaultActionEvent(), TheliaEvents::COUPON_CLEAR_ALL);
}
/**
* Coupon consuming.
*/
public function consumeAction(EventDispatcherInterface $eventDispatcher)
{
$this->checkCartNotEmpty($eventDispatcher);
$message = false;
$couponCodeForm = $this->createForm(FrontForm::COUPON_CONSUME);
try {
$form = $this->validateForm($couponCodeForm, 'post');
$couponCode = $form->get('coupon-code')->getData();
if (null === $couponCode || empty($couponCode)) {
$message = true;
throw new \Exception(
$this->getTranslator()->trans(
'Coupon code can\'t be empty',
[],
Front::MESSAGE_DOMAIN
)
);
}
$couponConsumeEvent = new CouponConsumeEvent($couponCode);
// Dispatch Event to the Action
$eventDispatcher->dispatch($couponConsumeEvent, TheliaEvents::COUPON_CONSUME);
/* recalculate postage amount */
$order = $this->getSession()->getOrder();
if (null !== $order) {
$deliveryModule = $order->getModuleRelatedByDeliveryModuleId();
$deliveryAddress = AddressQuery::create()->findPk($order->getChoosenDeliveryAddress());
if (null !== $deliveryModule && null !== $deliveryAddress) {
$moduleInstance = $deliveryModule->getDeliveryModuleInstance($this->container);
$orderEvent = new OrderEvent($order);
try {
$deliveryPostageEvent = new DeliveryPostageEvent(
$moduleInstance,
$this->getSession()->getSessionCart($eventDispatcher),
$deliveryAddress
);
$eventDispatcher->dispatch(
$deliveryPostageEvent,
TheliaEvents::MODULE_DELIVERY_GET_POSTAGE
);
$postage = $deliveryPostageEvent->getPostage();
$orderEvent->setPostage($postage->getAmount());
$orderEvent->setPostageTax($postage->getAmountTax());
$orderEvent->setPostageTaxRuleTitle($postage->getTaxRuleTitle());
$eventDispatcher->dispatch($orderEvent, TheliaEvents::ORDER_SET_POSTAGE);
} catch (\Exception $ex) {
// The postage has been chosen, but changes dues to coupon causes an exception.
// Reset the postage data in the order
$orderEvent->setDeliveryModule(0);
$eventDispatcher->dispatch($orderEvent, TheliaEvents::ORDER_SET_DELIVERY_MODULE);
}
}
}
return $this->generateSuccessRedirect($couponCodeForm);
} catch (FormValidationException $e) {
$message = $this->getTranslator()->trans(
'Please check your coupon code: %message',
['%message' => $e->getMessage()],
Front::MESSAGE_DOMAIN
);
} catch (UnmatchableConditionException $e) {
$message = $this->getTranslator()->trans(
'You should <a href="%sign">sign in</a> or <a href="%register">register</a> to use this coupon',
[
'%sign' => $this->retrieveUrlFromRouteId('customer.login.view'),
'%register' => $this->retrieveUrlFromRouteId('customer.create.view'),
],
Front::MESSAGE_DOMAIN
);
} catch (PropelException $e) {
$this->getParserContext()->setGeneralError($e->getMessage());
} catch (\Exception $e) {
$message = $this->getTranslator()->trans(
'Sorry, an error occurred: %message',
['%message' => $e->getMessage()],
Front::MESSAGE_DOMAIN
);
}
if ($message !== false) {
Tlog::getInstance()->error(
sprintf('Error during order delivery process : %s. Exception was %s', $message, $e->getMessage())
);
$couponCodeForm->setErrorMessage($message);
$this->getParserContext()
->addForm($couponCodeForm)
->setGeneralError($message);
}
return $this->generateErrorRedirect($couponCodeForm);
}
}

View File

@@ -0,0 +1,595 @@
<?php
/*
* This file is part of the Thelia package.
* http://www.thelia.net
*
* (c) OpenStudio <info@thelia.net>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Front\Controller;
use Front\Front;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Thelia\Controller\Front\BaseFrontController;
use Thelia\Core\Event\Customer\CustomerCreateOrUpdateEvent;
use Thelia\Core\Event\Customer\CustomerLoginEvent;
use Thelia\Core\Event\DefaultActionEvent;
use Thelia\Core\Event\LostPasswordEvent;
use Thelia\Core\Event\Newsletter\NewsletterEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Security\Authentication\CustomerUsernamePasswordFormAuthenticator;
use Thelia\Core\Security\Exception\AuthenticationException;
use Thelia\Core\Security\Exception\CustomerNotConfirmedException;
use Thelia\Core\Security\Exception\UsernameNotFoundException;
use Thelia\Core\Security\Exception\WrongPasswordException;
use Thelia\Form\CustomerLogin;
use Thelia\Form\Definition\FrontForm;
use Thelia\Form\Exception\FormValidationException;
use Thelia\Log\Tlog;
use Thelia\Model\ConfigQuery;
use Thelia\Model\Customer;
use Thelia\Model\CustomerQuery;
use Thelia\Model\Event\CustomerEvent;
use Thelia\Model\Newsletter;
use Thelia\Model\NewsletterQuery;
use Thelia\Tools\RememberMeTrait;
use Thelia\Tools\URL;
/**
* Class CustomerController.
*
* @author Manuel Raynaud <manu@raynaud.io>
*/
class CustomerController extends BaseFrontController
{
use RememberMeTrait;
/**
* Display the register template if no customer logged.
*/
public function viewLoginAction()
{
if ($this->getSecurityContext()->hasCustomerUser()) {
// Redirect to home page
return $this->generateRedirect(URL::getInstance()->getIndexPage());
}
return $this->render('login');
}
/**
* Display the register template if no customer logged.
*/
public function viewRegisterAction()
{
if ($this->getSecurityContext()->hasCustomerUser()) {
// Redirect to home page
return $this->generateRedirect(URL::getInstance()->getIndexPage());
}
return $this->render('register');
}
public function newPasswordAction(EventDispatcherInterface $eventDispatcher)
{
$passwordLost = $this->createForm(FrontForm::CUSTOMER_LOST_PASSWORD);
if (!$this->getSecurityContext()->hasCustomerUser()) {
try {
$form = $this->validateForm($passwordLost);
$event = new LostPasswordEvent($form->get('email')->getData());
$eventDispatcher->dispatch($event, TheliaEvents::LOST_PASSWORD);
return $this->generateSuccessRedirect($passwordLost);
} catch (FormValidationException $e) {
$message = $this->getTranslator()->trans(
'Please check your input: %s',
[
'%s' => $e->getMessage(),
],
Front::MESSAGE_DOMAIN
);
} catch (\Exception $e) {
$message = $this->getTranslator()->trans(
'Sorry, an error occurred: %s',
[
'%s' => $e->getMessage(),
],
Front::MESSAGE_DOMAIN
);
}
if ($message !== false) {
Tlog::getInstance()->error(
sprintf(
'Error during customer creation process : %s. Exception was %s',
$message,
$e->getMessage()
)
);
}
} else {
$message = $this->getTranslator()->trans(
"You're currently logged in. Please log out before requesting a new password.",
[],
Front::MESSAGE_DOMAIN
);
}
$passwordLost->setErrorMessage($message);
$this->getParserContext()
->addForm($passwordLost)
->setGeneralError($message)
;
// Redirect to error URL if defined
if ($passwordLost->hasErrorUrl()) {
return $this->generateErrorRedirect($passwordLost);
}
}
public function newPasswordSentAction(): void
{
$this->getParser()->assign('password_sent', true);
}
/**
* Create a new customer.
* On success, redirect to success_url if exists, otherwise, display the same view again.
*/
public function createAction(EventDispatcherInterface $eventDispatcher)
{
if (!$this->getSecurityContext()->hasCustomerUser()) {
$customerCreation = $this->createForm(FrontForm::CUSTOMER_CREATE);
try {
$form = $this->validateForm($customerCreation, 'post');
$customerCreateEvent = $this->createEventInstance($form->getData());
$eventDispatcher->dispatch($customerCreateEvent, TheliaEvents::CUSTOMER_CREATEACCOUNT);
$newCustomer = $customerCreateEvent->getCustomer();
// Newsletter
if (true === $form->get('newsletter')->getData()) {
$newsletterEmail = $newCustomer->getEmail();
$nlEvent = new NewsletterEvent(
$newsletterEmail,
$this->getRequest()->getSession()->getLang()->getLocale()
);
$nlEvent->setFirstname($newCustomer->getFirstname());
$nlEvent->setLastname($newCustomer->getLastname());
// Security : Check if this new Email address already exist
if (null !== $newsletter = NewsletterQuery::create()->findOneByEmail($newsletterEmail)) {
$nlEvent->setId($newsletter->getId());
$eventDispatcher->dispatch($nlEvent, TheliaEvents::NEWSLETTER_UPDATE);
} else {
$eventDispatcher->dispatch($nlEvent, TheliaEvents::NEWSLETTER_SUBSCRIBE);
}
}
if (ConfigQuery::isCustomerEmailConfirmationEnable() && !$newCustomer->getEnable()) {
$response = $this->generateRedirectFromRoute('customer.login.view');
} else {
$this->processLogin($eventDispatcher, $customerCreateEvent->getCustomer());
$cart = $this->getSession()->getSessionCart($eventDispatcher);
if ($cart->getCartItems()->count() > 0) {
$response = $this->generateRedirectFromRoute('cart.view');
} else {
$response = $this->generateSuccessRedirect($customerCreation);
}
}
return $response;
} catch (FormValidationException $e) {
$message = $this->getTranslator()->trans(
'Please check your input: %s',
[
'%s' => $e->getMessage(),
],
Front::MESSAGE_DOMAIN
);
} catch (\Exception $e) {
$message = $this->getTranslator()->trans(
'Sorry, an error occured: %s',
[
'%s' => $e->getMessage(),
],
Front::MESSAGE_DOMAIN
);
}
Tlog::getInstance()->error(
sprintf(
'Error during customer creation process : %s. Exception was %s',
$message,
$e->getMessage()
)
);
$customerCreation->setErrorMessage($message);
$this->getParserContext()
->addForm($customerCreation)
->setGeneralError($message)
;
// Redirect to error URL if defined
if ($customerCreation->hasErrorUrl()) {
return $this->generateErrorRedirect($customerCreation);
}
}
}
/**
* Prepare customer data update.
*/
public function viewAction(): void
{
$this->checkAuth();
/** @var Customer $customer */
$customer = $this->getSecurityContext()->getCustomerUser();
$newsletter = NewsletterQuery::create()->findOneByEmail($customer->getEmail());
$data = [
'id' => $customer->getId(),
'title' => $customer->getTitleId(),
'firstname' => $customer->getFirstName(),
'lastname' => $customer->getLastName(),
'email' => $customer->getEmail(),
'email_confirm' => $customer->getEmail(),
'lang_id' => $customer->getLangId(),
'newsletter' => $newsletter instanceof Newsletter ? !$newsletter->getUnsubscribed() : false,
];
$customerProfileUpdateForm = $this->createForm(FrontForm::CUSTOMER_PROFILE_UPDATE, FormType::class, $data);
// Pass it to the parser
$this->getParserContext()->addForm($customerProfileUpdateForm);
}
public function updatePasswordAction(EventDispatcherInterface $eventDispatcher)
{
if ($this->getSecurityContext()->hasCustomerUser()) {
$customerPasswordUpdateForm = $this->createForm(FrontForm::CUSTOMER_PASSWORD_UPDATE);
try {
/** @var Customer $customer */
$customer = $this->getSecurityContext()->getCustomerUser();
$form = $this->validateForm($customerPasswordUpdateForm, 'post');
$customerChangeEvent = $this->createEventInstance($form->getData());
$customerChangeEvent->setCustomer($customer);
$eventDispatcher->dispatch($customerChangeEvent, TheliaEvents::CUSTOMER_UPDATEPROFILE);
return $this->generateSuccessRedirect($customerPasswordUpdateForm);
} catch (FormValidationException $e) {
$message = $this->getTranslator()->trans(
'Please check your input: %s',
[
'%s' => $e->getMessage(),
],
Front::MESSAGE_DOMAIN
);
} catch (\Exception $e) {
$message = $this->getTranslator()->trans(
'Sorry, an error occured: %s',
[
'%s' => $e->getMessage(),
],
Front::MESSAGE_DOMAIN
);
}
Tlog::getInstance()->error(
sprintf(
'Error during customer password modification process : %s.',
$message
)
);
$customerPasswordUpdateForm->setErrorMessage($message);
$this->getParserContext()
->addForm($customerPasswordUpdateForm)
->setGeneralError($message)
;
// Redirect to error URL if defined
if ($customerPasswordUpdateForm->hasErrorUrl()) {
return $this->generateErrorRedirect($customerPasswordUpdateForm);
}
}
}
public function updateAction(EventDispatcherInterface $eventDispatcher)
{
if ($this->getSecurityContext()->hasCustomerUser()) {
$customerProfileUpdateForm = $this->createForm(FrontForm::CUSTOMER_PROFILE_UPDATE);
try {
/** @var Customer $customer */
$customer = $this->getSecurityContext()->getCustomerUser();
$newsletterOldEmail = $customer->getEmail();
$form = $this->validateForm($customerProfileUpdateForm, 'post');
$customerChangeEvent = $this->createEventInstance($form->getData());
$customerChangeEvent->setCustomer($customer);
$customerChangeEvent->setEmailUpdateAllowed(
((int) ConfigQuery::read('customer_change_email', 0)) ? true : false
);
$eventDispatcher->dispatch($customerChangeEvent, TheliaEvents::CUSTOMER_UPDATEPROFILE);
$updatedCustomer = $customerChangeEvent->getCustomer();
// Newsletter
if (true === $form->get('newsletter')->getData()) {
$nlEvent = new NewsletterEvent(
$updatedCustomer->getEmail(),
$this->getRequest()->getSession()->getLang()->getLocale()
);
$nlEvent->setFirstname($updatedCustomer->getFirstname());
$nlEvent->setLastname($updatedCustomer->getLastname());
if (null !== $newsletter = NewsletterQuery::create()->findOneByEmail($newsletterOldEmail)) {
$nlEvent->setId($newsletter->getId());
$eventDispatcher->dispatch($nlEvent, TheliaEvents::NEWSLETTER_UPDATE);
} else {
$eventDispatcher->dispatch($nlEvent, TheliaEvents::NEWSLETTER_SUBSCRIBE);
}
} else {
if (null !== $newsletter = NewsletterQuery::create()->findOneByEmail($newsletterOldEmail)) {
$nlEvent = new NewsletterEvent(
$updatedCustomer->getEmail(),
$this->getRequest()->getSession()->getLang()->getLocale()
);
$nlEvent->setId($newsletter->getId());
$eventDispatcher->dispatch($nlEvent, TheliaEvents::NEWSLETTER_UNSUBSCRIBE);
}
}
$this->processLogin($eventDispatcher, $updatedCustomer);
return $this->generateSuccessRedirect($customerProfileUpdateForm);
} catch (FormValidationException $e) {
$message = $this->getTranslator()->trans(
'Please check your input: %s',
[
'%s' => $e->getMessage(),
],
Front::MESSAGE_DOMAIN
);
} catch (\Exception $e) {
$message = $this->getTranslator()->trans(
'Sorry, an error occured: %s',
[
'%s' => $e->getMessage(),
],
Front::MESSAGE_DOMAIN
);
}
Tlog::getInstance()->error(sprintf('Error during customer modification process : %s.', $message));
$customerProfileUpdateForm->setErrorMessage($message);
$this->getParserContext()
->addForm($customerProfileUpdateForm)
->setGeneralError($message)
;
// Redirect to error URL if defined
if ($customerProfileUpdateForm->hasErrorUrl()) {
return $this->generateErrorRedirect($customerProfileUpdateForm);
}
}
}
/**
* Perform user login. On a successful login, the user is redirected to the URL
* found in the success_url form parameter, or / if none was found.
*
* If login is not successfull, the same view is displayed again.
*/
public function loginAction(EventDispatcherInterface $eventDispatcher)
{
if (!$this->getSecurityContext()->hasCustomerUser()) {
$request = $this->getRequest();
$customerLoginForm = $this->createForm(CustomerLogin::class);
try {
$form = $this->validateForm($customerLoginForm, 'post');
// If User is a new customer
if ($form->get('account')->getData() == 0 && $form->get('email')->getErrors()->count() == 0) {
return $this->generateRedirectFromRoute(
'customer.create.process',
['email' => $form->get('email')->getData()]
);
}
try {
$authenticator = new CustomerUsernamePasswordFormAuthenticator($request, $customerLoginForm);
/** @var Customer $customer */
$customer = $authenticator->getAuthentifiedUser();
$this->processLogin($eventDispatcher, $customer);
if ((int) $form->get('remember_me')->getData() > 0) {
// If a remember me field if present and set in the form, create
// the cookie thant store "remember me" information
$this->createRememberMeCookie(
$customer,
$this->getRememberMeCookieName(),
$this->getRememberMeCookieExpiration()
);
}
return $this->generateSuccessRedirect($customerLoginForm);
} catch (UsernameNotFoundException $e) {
$message = $this->getTranslator()->trans(
'Wrong email or password. Please try again',
[],
Front::MESSAGE_DOMAIN
);
} catch (WrongPasswordException $e) {
$message = $this->getTranslator()->trans(
'Wrong email or password. Please try again',
[],
Front::MESSAGE_DOMAIN
);
} catch (CustomerNotConfirmedException $e) {
if ($e->getUser() !== null) {
// Send the confirmation email again
$eventDispatcher->dispatch(
new CustomerEvent($e->getUser()),
TheliaEvents::SEND_ACCOUNT_CONFIRMATION_EMAIL
);
}
$message = $this->getTranslator()->trans(
'Your account is not yet confirmed. A confirmation email has been sent to your email address, please check your mailbox',
[],
Front::MESSAGE_DOMAIN
);
} catch (AuthenticationException $e) {
$message = $this->getTranslator()->trans(
'Wrong email or password. Please try again',
[],
Front::MESSAGE_DOMAIN
);
}
} catch (FormValidationException $e) {
$message = $this->getTranslator()->trans(
'Please check your input: %s',
['%s' => $e->getMessage()],
Front::MESSAGE_DOMAIN
);
} catch (\Exception $e) {
$message = $this->getTranslator()->trans(
'Sorry, an error occured: %s',
['%s' => $e->getMessage()],
Front::MESSAGE_DOMAIN
);
}
Tlog::getInstance()->error(
sprintf(
'Error during customer login process : %s. Exception was %s',
$message,
$e->getMessage()
)
);
$customerLoginForm->setErrorMessage($message);
$this->getParserContext()->addForm($customerLoginForm);
if ($customerLoginForm->hasErrorUrl()) {
return $this->generateErrorRedirect($customerLoginForm);
}
}
}
/**
* Perform customer logout.
*/
public function logoutAction(EventDispatcherInterface $eventDispatcher)
{
if ($this->getSecurityContext()->hasCustomerUser()) {
$eventDispatcher->dispatch(new DefaultActionEvent(), TheliaEvents::CUSTOMER_LOGOUT);
}
$this->clearRememberMeCookie($this->getRememberMeCookieName());
// Redirect to home page
return $this->generateRedirect(URL::getInstance()->getIndexPage());
}
/**
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function confirmCustomerAction($token)
{
/** @var Customer $customer */
if (null === $customer = CustomerQuery::create()->findOneByConfirmationToken($token)) {
throw new NotFoundHttpException();
}
$customer
->setEnable(true)
->save()
;
// Clear form error context
return $this->generateRedirectFromRoute('customer.login.view', ['validation_done' => 1]);
}
/**
* Dispatch event for customer login action.
*/
protected function processLogin(EventDispatcherInterface $eventDispatcher, Customer $customer): void
{
$eventDispatcher->dispatch(new CustomerLoginEvent($customer), TheliaEvents::CUSTOMER_LOGIN);
}
/**
* @return \Thelia\Core\Event\Customer\CustomerCreateOrUpdateEvent
*/
private function createEventInstance($data)
{
$customerCreateEvent = new CustomerCreateOrUpdateEvent(
$data['title'] ?? null,
$data['firstname'] ?? null,
$data['lastname'] ?? null,
$data['address1'] ?? null,
$data['address2'] ?? null,
$data['address3'] ?? null,
$data['phone'] ?? null,
$data['cellphone'] ?? null,
$data['zipcode'] ?? null,
$data['city'] ?? null,
$data['country'] ?? null,
$data['email'] ?? null,
$data['password'] ?? null,
$data['lang_id'] ?? $this->getSession()->getLang()->getId(),
$data['reseller'] ?? null,
$data['sponsor'] ?? null,
$data['discount'] ?? null,
$data['company'] ?? null,
null,
$data['state'] ?? null
);
return $customerCreateEvent;
}
protected function getRememberMeCookieName()
{
return ConfigQuery::read('customer_remember_me_cookie_name', 'crmcn');
}
protected function getRememberMeCookieExpiration()
{
return ConfigQuery::read('customer_remember_me_cookie_expiration', 2592000 /* 1 month */);
}
}

View File

@@ -0,0 +1,196 @@
<?php
/*
* This file is part of the Thelia package.
* http://www.thelia.net
*
* (c) OpenStudio <info@thelia.net>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Front\Controller;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Thelia\Controller\Front\BaseFrontController;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Core\HttpFoundation\Response;
use Thelia\Model\BrandQuery;
use Thelia\Model\CategoryQuery;
use Thelia\Model\ConfigQuery;
use Thelia\Model\FolderQuery;
use Thelia\Model\Lang;
use Thelia\Model\LangQuery;
/**
* Controller uses to generate RSS Feeds.
*
* A default cache of 2 hours is used to avoid attack. You can flush cache if you have `ADMIN` role and pass flush=1 in
* query string parameter.
*
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
class FeedController extends BaseFrontController
{
/**
* Folder name for feeds cache.
*/
public const FEED_CACHE_DIR = 'feeds';
/**
* Key prefix for feed cache.
*/
public const FEED_CACHE_KEY = 'feed';
/**
* render the RSS feed.
*
* @param $context string The context of the feed : catalog, content. default: catalog
* @param $lang string The lang of the feed : fr_FR, en_US, ... default: default language of the site
* @param $id string The id of the parent element. The id of the main parent category for catalog context.
* The id of the content folder for content context
*
* @throws \RuntimeException
*
* @return Response
*/
public function generateAction($context, $lang, $id)
{
/** @var Request $request */
$request = $this->getRequest();
// context
if ('' === $context) {
$context = 'catalog';
} elseif (!\in_array($context, ['catalog', 'content', 'brand'])) {
$this->pageNotFound();
}
// the locale : fr_FR, en_US,
if ('' !== $lang) {
if (!$this->checkLang($lang)) {
$this->pageNotFound();
}
} else {
try {
$lang = Lang::getDefaultLanguage();
$lang = $lang->getLocale();
} catch (\RuntimeException $ex) {
// @todo generate error page
throw new \RuntimeException('No default language is defined. Please define one.');
}
}
if (null === $lang = LangQuery::create()->findOneByLocale($lang)) {
$this->pageNotFound();
}
$lang = $lang->getId();
// check if element exists and is visible
if ('' !== $id) {
if (false === $this->checkId($context, $id)) {
$this->pageNotFound();
}
}
$flush = $request->query->get('flush', '');
/** @var AdapterInterface $cacheAdapter */
$cacheAdapter = $this->container->get('thelia.cache');
$cacheKey = self::FEED_CACHE_KEY.$lang.$context;
$cacheItem = $cacheAdapter->getItem($cacheKey);
if (!$cacheItem->isHit() || $flush) {
$cacheExpire = (int) (ConfigQuery::read('feed_ttl', '7200')) ?: 7200;
// render the view
$cacheContent = $this->renderRaw(
'feed',
[
'_context_' => $context,
'_lang_' => $lang,
'_id_' => $id,
]
);
$cacheItem->expiresAfter($cacheExpire);
$cacheItem->set($cacheContent);
$cacheAdapter->save($cacheItem);
}
$response = new Response();
$response->setContent($cacheItem->get());
$response->headers->set('Content-Type', 'application/rss+xml');
return $response;
}
/**
* get the cache directory for feeds.
*
* @return mixed|string
*/
protected function getCacheDir()
{
$cacheDir = $this->container->getParameter('kernel.cache_dir');
$cacheDir = rtrim($cacheDir, '/');
$cacheDir .= '/'.self::FEED_CACHE_DIR.'/';
return $cacheDir;
}
/**
* Check if current user has ADMIN role.
*
* @return bool
*/
protected function checkAdmin()
{
return $this->getSecurityContext()->hasAdminUser();
}
/**
* Check if a lang is used.
*
* @param $lang string The lang code. e.g.: fr
*
* @return bool true if the language is used, otherwise false
*/
private function checkLang($lang)
{
// load locals
$lang = LangQuery::create()
->findOneByLocale($lang);
return null !== $lang;
}
/**
* Check if the element exists and is visible.
*
* @param $context string catalog or content
* @param $id string id of the element
*
* @return bool
*/
private function checkId($context, $id)
{
$ret = false;
if (is_numeric($id)) {
if ('catalog' === $context) {
$cat = CategoryQuery::create()->findPk($id);
$ret = (null !== $cat && $cat->getVisible());
} elseif ('brand' === $context) {
$brand = BrandQuery::create()->findPk($id);
$ret = (null !== $brand && $brand->getVisible());
} else {
$folder = FolderQuery::create()->findPk($id);
$ret = (null !== $folder && $folder->getVisible());
}
}
return $ret;
}
}

View File

@@ -0,0 +1,154 @@
<?php
/*
* This file is part of the Thelia package.
* http://www.thelia.net
*
* (c) OpenStudio <info@thelia.net>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Front\Controller;
use Front\Front;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Thelia\Controller\Front\BaseFrontController;
use Thelia\Core\Event\Newsletter\NewsletterEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Form\Definition\FrontForm;
use Thelia\Log\Tlog;
use Thelia\Model\Customer;
use Thelia\Model\NewsletterQuery;
/**
* Class NewsletterController.
*
* @author Manuel Raynaud <manu@raynaud.io>, Franck Allimant <franck@cqfdev.fr>
*/
class NewsletterController extends BaseFrontController
{
/**
* @since 2.3.0-alpha2
*/
public function unsubscribeAction(EventDispatcherInterface $eventDispatcher)
{
$errorMessage = false;
$newsletterForm = $this->createForm(FrontForm::NEWSLETTER_UNSUBSCRIBE);
try {
$form = $this->validateForm($newsletterForm);
$email = $form->get('email')->getData();
if (null !== $newsletter = NewsletterQuery::create()->findOneByEmail($email)) {
$event = new NewsletterEvent(
$email,
$this->getRequest()->getSession()->getLang()->getLocale()
);
$event->setId($newsletter->getId());
$eventDispatcher->dispatch($event, TheliaEvents::NEWSLETTER_UNSUBSCRIBE);
// If a success URL is defined in the form, redirect to it, otherwise use the defaut view
if ($newsletterForm->hasSuccessUrl() && !$this->getRequest()->isXmlHttpRequest()) {
return $this->generateSuccessRedirect($newsletterForm);
}
}
} catch (\Exception $e) {
$errorMessage = $e->getMessage();
Tlog::getInstance()->error(sprintf('Error during newsletter unsubscription : %s', $errorMessage));
$newsletterForm->setErrorMessage($errorMessage);
}
// If Ajax Request
if ($this->getRequest()->isXmlHttpRequest()) {
return new JsonResponse([
'success' => ($errorMessage) ? false : true,
'message' => ($errorMessage) ? $errorMessage : $this->getTranslator()->trans(
'Your subscription to our newsletter has been canceled.',
[],
Front::MESSAGE_DOMAIN
),
], ($errorMessage) ? 500 : 200);
}
$this->getParserContext()
->setGeneralError($errorMessage)
->addForm($newsletterForm);
// If an error URL is defined in the form, redirect to it, otherwise use the defaut view
if ($errorMessage && $newsletterForm->hasErrorUrl()) {
return $this->generateErrorRedirect($newsletterForm);
}
}
public function subscribeAction(EventDispatcherInterface $eventDispatcher)
{
$errorMessage = false;
$newsletterForm = $this->createForm(FrontForm::NEWSLETTER);
try {
$form = $this->validateForm($newsletterForm);
$event = new NewsletterEvent(
$form->get('email')->getData(),
$this->getRequest()->getSession()->getLang()->getLocale()
);
/** @var Customer $customer */
if (null !== $customer = $this->getSecurityContext()->getCustomerUser()) {
$event
->setFirstname($customer->getFirstname())
->setLastname($customer->getLastname())
;
} else {
$event
->setFirstname($form->get('firstname')->getData())
->setLastname($form->get('lastname')->getData())
;
}
$eventDispatcher->dispatch($event, TheliaEvents::NEWSLETTER_SUBSCRIBE);
// If a success URL is defined in the form, redirect to it, otherwise use the defaut view
if ($newsletterForm->hasSuccessUrl() && !$this->getRequest()->isXmlHttpRequest()) {
return $this->generateSuccessRedirect($newsletterForm);
}
} catch (\Exception $e) {
$errorMessage = $e->getMessage();
Tlog::getInstance()->error(sprintf('Error during newsletter subscription : %s', $errorMessage));
$newsletterForm->setErrorMessage($errorMessage);
}
// If Ajax Request
if ($this->getRequest()->isXmlHttpRequest()) {
return new JsonResponse([
'success' => ($errorMessage) ? false : true,
'message' => ($errorMessage) ? $errorMessage : $this->getTranslator()->trans(
"Thanks for signing up! We'll keep you posted whenever we have any new updates.",
[],
Front::MESSAGE_DOMAIN
),
], ($errorMessage) ? 500 : 200);
}
$this->getParserContext()
->setGeneralError($errorMessage)
->addForm($newsletterForm);
// If an error URL is defined in the form, redirect to it, otherwise use the defaut view
if ($errorMessage && $newsletterForm->hasErrorUrl()) {
return $this->generateErrorRedirect($newsletterForm);
}
}
}

View File

@@ -0,0 +1,591 @@
<?php
/*
* This file is part of the Thelia package.
* http://www.thelia.net
*
* (c) OpenStudio <info@thelia.net>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Front\Controller;
use Front\Front;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\Exception\PropelException;
use Symfony\Component\HttpFoundation\Response as BaseResponse;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Thelia\Controller\Front\BaseFrontController;
use Thelia\Core\Event\Delivery\DeliveryPostageEvent;
use Thelia\Core\Event\Order\OrderEvent;
use Thelia\Core\Event\Product\VirtualProductOrderDownloadResponseEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Exception\TheliaProcessException;
use Thelia\Form\Definition\FrontForm;
use Thelia\Form\Exception\FormValidationException;
use Thelia\Log\Tlog;
use Thelia\Model\Address;
use Thelia\Model\AddressQuery;
use Thelia\Model\AreaDeliveryModuleQuery;
use Thelia\Model\ConfigQuery;
use Thelia\Model\ModuleQuery;
use Thelia\Model\Order;
use Thelia\Model\OrderProductQuery;
use Thelia\Model\OrderQuery;
use Thelia\Module\AbstractDeliveryModule;
use Thelia\Module\Exception\DeliveryException;
/**
* Class OrderController.
*
* @author Etienne Roudeix <eroudeix@openstudio.fr>
*/
class OrderController extends BaseFrontController
{
/**
* Check if the cart contains only virtual products.
*/
public function deliverView(EventDispatcherInterface $eventDispatcher)
{
$this->checkAuth();
$this->checkCartNotEmpty($eventDispatcher);
// check if the cart contains only virtual products
$cart = $this->getSession()->getSessionCart($eventDispatcher);
$deliveryAddress = $this->getCustomerAddress();
if ($cart->isVirtual()) {
if (null !== $deliveryAddress) {
$deliveryModule = ModuleQuery::create()->retrieveVirtualProductDelivery($this->container);
if (false === $deliveryModule) {
Tlog::getInstance()->error(
$this->getTranslator()->trans(
'To enable the virtual product feature, the VirtualProductDelivery module should be activated',
[],
Front::MESSAGE_DOMAIN
)
);
} elseif (\count($deliveryModule) == 1) {
return $this->registerVirtualProductDelivery($eventDispatcher, $deliveryModule[0], $deliveryAddress);
}
}
}
return $this->render(
'order-delivery',
[
'delivery_address_id' => (null !== $deliveryAddress) ? $deliveryAddress->getId() : null,
]
);
}
/**
* @param AbstractDeliveryModule $moduleInstance
* @param Address $deliveryAddress
*
* @return \Symfony\Component\HttpFoundation\Response
*/
private function registerVirtualProductDelivery(EventDispatcherInterface $eventDispatcher, $moduleInstance, $deliveryAddress)
{
/* get postage amount */
$deliveryModule = $moduleInstance->getModuleModel();
$cart = $this->getSession()->getSessionCart($eventDispatcher);
$deliveryPostageEvent = new DeliveryPostageEvent($moduleInstance, $cart, $deliveryAddress);
$eventDispatcher->dispatch(
$deliveryPostageEvent,
TheliaEvents::MODULE_DELIVERY_GET_POSTAGE
);
$postage = $deliveryPostageEvent->getPostage();
$orderEvent = $this->getOrderEvent();
$orderEvent->setDeliveryAddress($deliveryAddress->getId());
$orderEvent->setDeliveryModule($deliveryModule->getId());
$orderEvent->setPostage($postage->getAmount());
$orderEvent->setPostageTax($postage->getAmountTax());
$orderEvent->setPostageTaxRuleTitle($postage->getTaxRuleTitle());
$eventDispatcher->dispatch($orderEvent, TheliaEvents::ORDER_SET_DELIVERY_ADDRESS);
$eventDispatcher->dispatch($orderEvent, TheliaEvents::ORDER_SET_DELIVERY_MODULE);
$eventDispatcher->dispatch($orderEvent, TheliaEvents::ORDER_SET_POSTAGE);
return $this->generateRedirectFromRoute('order.invoice');
}
/**
* set delivery address
* set delivery module.
*/
public function deliver(EventDispatcherInterface $eventDispatcher)
{
$this->checkAuth();
$this->checkCartNotEmpty($eventDispatcher);
$message = false;
$orderDelivery = $this->createForm(FrontForm::ORDER_DELIVER);
try {
$form = $this->validateForm($orderDelivery, 'post');
$deliveryAddressId = $form->get('delivery-address')->getData();
$deliveryModuleId = $form->get('delivery-module')->getData();
$deliveryAddress = AddressQuery::create()->findPk($deliveryAddressId);
$deliveryModule = ModuleQuery::create()->findPk($deliveryModuleId);
/* check that the delivery address belongs to the current customer */
if ($deliveryAddress->getCustomerId() !== $this->getSecurityContext()->getCustomerUser()->getId()) {
throw new \Exception(
$this->getTranslator()->trans(
'Delivery address does not belong to the current customer',
[],
Front::MESSAGE_DOMAIN
)
);
}
/* check that the delivery module fetches the delivery address area */
if (null === AreaDeliveryModuleQuery::create()->findByCountryAndModule(
$deliveryAddress->getCountry(),
$deliveryModule,
$deliveryAddress->getState()
)) {
throw new \Exception(
$this->getTranslator()->trans(
'Delivery module cannot be use with selected delivery address',
[],
Front::MESSAGE_DOMAIN
)
);
}
/* get postage amount */
$moduleInstance = $deliveryModule->getDeliveryModuleInstance($this->container);
$cart = $this->getSession()->getSessionCart($eventDispatcher);
$deliveryPostageEvent = new DeliveryPostageEvent($moduleInstance, $cart, $deliveryAddress);
$eventDispatcher->dispatch(
$deliveryPostageEvent,
TheliaEvents::MODULE_DELIVERY_GET_POSTAGE
);
if (!$deliveryPostageEvent->isValidModule() || null === $deliveryPostageEvent->getPostage()) {
throw new DeliveryException(
$this->getTranslator()->trans('The delivery module is not valid.', [], Front::MESSAGE_DOMAIN)
);
}
$postage = $deliveryPostageEvent->getPostage();
$orderEvent = $this->getOrderEvent();
$orderEvent->setDeliveryAddress($deliveryAddressId);
$orderEvent->setDeliveryModule($deliveryModuleId);
$orderEvent->setPostage($postage->getAmount());
$orderEvent->setPostageTax($postage->getAmountTax());
$orderEvent->setPostageTaxRuleTitle($postage->getTaxRuleTitle());
$eventDispatcher->dispatch($orderEvent, TheliaEvents::ORDER_SET_DELIVERY_ADDRESS);
$eventDispatcher->dispatch($orderEvent, TheliaEvents::ORDER_SET_DELIVERY_MODULE);
$eventDispatcher->dispatch($orderEvent, TheliaEvents::ORDER_SET_POSTAGE);
return $this->generateRedirectFromRoute('order.invoice');
} catch (FormValidationException $e) {
$message = $this->getTranslator()->trans(
'Please check your input: %s',
['%s' => $e->getMessage()],
Front::MESSAGE_DOMAIN
);
} catch (PropelException $e) {
$this->getParserContext()->setGeneralError($e->getMessage());
} catch (\Exception $e) {
$message = $this->getTranslator()->trans(
'Sorry, an error occured: %s',
['%s' => $e->getMessage()],
Front::MESSAGE_DOMAIN
);
}
if ($message !== false) {
Tlog::getInstance()->error(
sprintf('Error during order delivery process : %s. Exception was %s', $message, $e->getMessage())
);
$orderDelivery->setErrorMessage($message);
$this->getParserContext()
->addForm($orderDelivery)
->setGeneralError($message)
;
}
}
/**
* set invoice address
* set payment module.
*/
public function invoice(EventDispatcherInterface $eventDispatcher)
{
$this->checkAuth();
$this->checkCartNotEmpty($eventDispatcher);
$this->checkValidDelivery();
$message = false;
$orderPayment = $this->createForm(FrontForm::ORDER_PAYMENT);
try {
$form = $this->validateForm($orderPayment, 'post');
$invoiceAddressId = $form->get('invoice-address')->getData();
$paymentModuleId = $form->get('payment-module')->getData();
/* check that the invoice address belongs to the current customer */
$invoiceAddress = AddressQuery::create()->findPk($invoiceAddressId);
if ($invoiceAddress->getCustomerId() !== $this->getSecurityContext()->getCustomerUser()->getId()) {
throw new \Exception(
$this->getTranslator()->trans(
'Invoice address does not belong to the current customer',
[],
Front::MESSAGE_DOMAIN
)
);
}
$orderEvent = $this->getOrderEvent();
$orderEvent->setInvoiceAddress($invoiceAddressId);
$orderEvent->setPaymentModule($paymentModuleId);
$eventDispatcher->dispatch($orderEvent, TheliaEvents::ORDER_SET_INVOICE_ADDRESS);
$eventDispatcher->dispatch($orderEvent, TheliaEvents::ORDER_SET_PAYMENT_MODULE);
return $this->generateRedirectFromRoute('order.payment.process');
} catch (FormValidationException $e) {
$message = $this->getTranslator()->trans(
'Please check your input: %s',
['%s' => $e->getMessage()],
Front::MESSAGE_DOMAIN
);
} catch (PropelException $e) {
$this->getParserContext()->setGeneralError($e->getMessage());
} catch (\Exception $e) {
$message = $this->getTranslator()->trans(
'Sorry, an error occured: %s',
['%s' => $e->getMessage()],
Front::MESSAGE_DOMAIN
);
}
if ($message !== false) {
Tlog::getInstance()->error(
sprintf('Error during order payment process : %s. Exception was %s', $message, $e->getMessage())
);
$orderPayment->setErrorMessage($message);
$this->getParserContext()
->addForm($orderPayment)
->setGeneralError($message)
;
}
return $this->generateErrorRedirect($orderPayment);
}
public function pay(EventDispatcherInterface $eventDispatcher)
{
/* check customer */
$this->checkAuth();
/* check cart count */
$this->checkCartNotEmpty($eventDispatcher);
/* check stock not empty */
if (true === ConfigQuery::checkAvailableStock()) {
if (null !== $response = $this->checkStockNotEmpty($eventDispatcher)) {
return $response;
}
}
/* check delivery address and module */
$this->checkValidDelivery();
/* check invoice address and payment module */
$this->checkValidInvoice();
$orderEvent = $this->getOrderEvent();
$eventDispatcher->dispatch($orderEvent, TheliaEvents::ORDER_PAY);
$placedOrder = $orderEvent->getPlacedOrder();
if (null !== $placedOrder && null !== $placedOrder->getId()) {
/* order has been placed */
if ($orderEvent->hasResponse()) {
return $orderEvent->getResponse();
}
return $this->generateRedirectFromRoute(
'order.placed',
[],
['order_id' => $orderEvent->getPlacedOrder()->getId()]
);
}
/* order has not been placed */
return $this->generateRedirectFromRoute('cart.view');
}
public function orderPlaced(EventDispatcherInterface $eventDispatcher, $order_id): void
{
/* check if the placed order matched the customer */
$placedOrder = OrderQuery::create()->findPk(
$this->getRequest()->attributes->get('order_id')
);
if (null === $placedOrder) {
throw new TheliaProcessException(
$this->getTranslator()->trans(
'No placed order',
[],
Front::MESSAGE_DOMAIN
),
TheliaProcessException::NO_PLACED_ORDER,
$placedOrder
);
}
$customer = $this->getSecurityContext()->getCustomerUser();
if (null === $customer || $placedOrder->getCustomerId() !== $customer->getId()) {
throw new TheliaProcessException(
$this->getTranslator()->trans(
'Received placed order id does not belong to the current customer',
[],
Front::MESSAGE_DOMAIN
),
TheliaProcessException::PLACED_ORDER_ID_BAD_CURRENT_CUSTOMER,
$placedOrder
);
}
$eventDispatcher->dispatch($this->getOrderEvent(), TheliaEvents::ORDER_CART_CLEAR);
$this->getParserContext()->set('placed_order_id', $placedOrder->getId());
}
public function orderFailed($order_id, $message): void
{
if (empty($order_id)) {
// Fallback to request parameter if the method parameter is empty.
$order_id = $this->getRequest()->get('order_id');
}
$failedOrder = OrderQuery::create()->findPk($order_id);
if (null !== $failedOrder) {
$customer = $this->getSecurityContext()->getCustomerUser();
if (null === $customer || $failedOrder->getCustomerId() !== $customer->getId()) {
throw new TheliaProcessException(
$this->getTranslator()->trans(
'Received failed order id does not belong to the current customer',
[],
Front::MESSAGE_DOMAIN
),
TheliaProcessException::PLACED_ORDER_ID_BAD_CURRENT_CUSTOMER,
$failedOrder
);
}
} else {
Tlog::getInstance()->warning("Failed order ID '$order_id' not found.");
}
$this->getParserContext()
->set('failed_order_id', $order_id)
->set('failed_order_message', $message)
;
}
protected function getOrderEvent()
{
$order = $this->getOrder($this->getRequest());
return new OrderEvent($order);
}
public function getOrder(Request $request)
{
$session = $request->getSession();
if (null !== $order = $session->getOrder()) {
return $order;
}
$order = new Order();
$session->setOrder($order);
return $order;
}
public function viewAction($order_id)
{
$this->checkOrderCustomer($order_id);
return $this->render('account-order', ['order_id' => $order_id]);
}
public function generateInvoicePdf(EventDispatcherInterface $eventDispatcher, $order_id)
{
$this->checkOrderCustomer($order_id);
return $this->generateOrderPdf($eventDispatcher, $order_id, ConfigQuery::read('pdf_invoice_file', 'invoice'));
}
public function generateDeliveryPdf(EventDispatcherInterface $eventDispatcher, $order_id)
{
$this->checkOrderCustomer($order_id);
return $this->generateOrderPdf($eventDispatcher, $order_id, ConfigQuery::read('pdf_delivery_file', 'delivery'));
}
public function downloadVirtualProduct(EventDispatcherInterface $eventDispatcher, $order_product_id)
{
if (null !== $orderProduct = OrderProductQuery::create()->findPk($order_product_id)) {
$order = $orderProduct->getOrder();
if ($order->isPaid(false)) {
// check customer
$this->checkOrderCustomer($order->getId());
$virtualProductEvent = new VirtualProductOrderDownloadResponseEvent($orderProduct);
$eventDispatcher->dispatch(
$virtualProductEvent,
TheliaEvents::VIRTUAL_PRODUCT_ORDER_DOWNLOAD_RESPONSE
);
$response = $virtualProductEvent->getResponse();
if (!$response instanceof BaseResponse) {
throw new \RuntimeException('A Response must be added in the event TheliaEvents::VIRTUAL_PRODUCT_ORDER_DOWNLOAD_RESPONSE');
}
return $response;
}
}
throw new AccessDeniedHttpException();
}
private function checkOrderCustomer($order_id): void
{
$this->checkAuth();
$order = OrderQuery::create()->findPk($order_id);
$valid = true;
if ($order) {
$customerOrder = $order->getCustomer();
$customer = $this->getSecurityContext()->getCustomerUser();
if ($customerOrder->getId() != $customer->getId()) {
$valid = false;
}
} else {
$valid = false;
}
if (false === $valid) {
throw new AccessDeniedHttpException();
}
}
public function getDeliveryModuleListAjaxAction()
{
$this->checkXmlHttpRequest();
// Change the delivery address if customer has changed it
$address = null;
$session = $this->getSession();
$addressId = $this->getRequest()->get('address_id', null);
if (null !== $addressId && $addressId !== $session->getOrder()->getChoosenDeliveryAddress()) {
$address = AddressQuery::create()->findPk($addressId);
if (null !== $address && $address->getCustomerId() === $session->getCustomerUser()->getId()) {
$session->getOrder()->setChoosenDeliveryAddress($addressId);
}
}
$address = AddressQuery::create()->findPk($session->getOrder()->getChoosenDeliveryAddress());
$countryId = $address->getCountryId();
$stateId = $address->getStateId();
$args = [
'country' => $countryId,
'state' => $stateId,
'address' => $session->getOrder()->getChoosenDeliveryAddress(),
];
return $this->render('ajax/order-delivery-module-list', $args);
}
/**
* Redirect to cart view if at least one non product is out of stock.
*
* @return BaseResponse|null
*/
private function checkStockNotEmpty(EventDispatcherInterface $eventDispatcher)
{
$cart = $this->getSession()->getSessionCart($eventDispatcher);
$cartItems = $cart->getCartItems();
foreach ($cartItems as $cartItem) {
$pse = $cartItem->getProductSaleElements();
$product = $cartItem->getProduct();
if ($pse->getQuantity() <= 0 && $product->getVirtual() !== 1) {
return $this->generateRedirectFromRoute('cart.view');
}
}
return null;
}
/**
* Retrieve the chosen delivery address for a cart or the default customer address if not exists.
*
* @return Address|null
*/
protected function getCustomerAddress()
{
$deliveryAddress = null;
$addressId = $this->getSession()->getOrder()->getChoosenDeliveryAddress();
if (null === $addressId) {
$customer = $this->getSecurityContext()->getCustomerUser();
$deliveryAddress = AddressQuery::create()
->filterByCustomerId($customer->getId())
->orderByIsDefault(Criteria::DESC)
->findOne();
if (null !== $deliveryAddress) {
$this->getSession()->getOrder()->setChoosenDeliveryAddress(
$deliveryAddress->getId()
);
}
} else {
$deliveryAddress = AddressQuery::create()->findPk($addressId);
}
return $deliveryAddress;
}
}

View File

@@ -0,0 +1,144 @@
<?php
/*
* This file is part of the Thelia package.
* http://www.thelia.net
*
* (c) OpenStudio <info@thelia.net>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Front\Controller;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Thelia\Controller\Front\BaseFrontController;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Core\HttpFoundation\Response;
use Thelia\Model\ConfigQuery;
use Thelia\Model\LangQuery;
/**
* Controller uses to generate sitemap.xml.
*
* A default cache of 2 hours is used to avoid attack. You can flush cache if you have `ADMIN` role and pass flush=1 in
* query string parameter.
*
* You can generate sitemap according to specific language and/or specific context (catalog/content). You have to
* use ```lang``` and ```context``` query string parameters to do so. If a language is not used in website or if the
* context is not supported the page not found is displayed.
*
* {url}/sitemap?lang=fr&context=catalog will generate a sitemap for catalog (categories and products)
* for french language.
*
* @author Julien Chanséaume <jchanseaume@openstudio.fr>
*/
class SitemapController extends BaseFrontController
{
/**
* Folder name for sitemap cache.
*/
public const SITEMAP_CACHE_DIR = 'sitemap';
/**
* Key prefix for sitemap cache.
*/
public const SITEMAP_CACHE_KEY = 'sitemap';
/**
* @return Response
*/
public function generateAction()
{
/** @var Request $request */
$request = $this->getRequest();
// the locale : fr, en,
$lang = $request->query->get('lang', '');
if ('' !== $lang) {
if (!$this->checkLang($lang)) {
$this->pageNotFound();
}
}
// specific content : product, category, cms
$context = $request->query->get('context', '');
if (!\in_array($context, ['', 'catalog', 'content'])) {
$this->pageNotFound();
}
$flush = $request->query->get('flush', '');
/** @var AdapterInterface $cacheAdapter */
$cacheAdapter = $this->container->get('thelia.cache');
$cacheKey = self::SITEMAP_CACHE_KEY.$lang.$context;
$cacheItem = $cacheAdapter->getItem($cacheKey);
if (!$cacheItem->isHit() || $flush) {
$cacheExpire = (int) (ConfigQuery::read('sitemap_ttl', '7200')) ?: 7200;
// Render the view. Compression causes problems and is deactivated.
$cacheContent = $this->getParser(null)->render(
'sitemap.html',
[
'_lang_' => $lang,
'_context_' => $context,
],
false
);
$cacheItem->expiresAfter($cacheExpire);
$cacheItem->set($cacheContent);
$cacheAdapter->save($cacheItem);
}
$response = new Response();
$response->setContent($cacheItem->get());
$response->headers->set('Content-Type', 'application/xml');
return $response;
}
/**
* get the cache directory for sitemap.
*
* @return mixed|string
*/
protected function getCacheDir()
{
$cacheDir = $this->container->getParameter('kernel.cache_dir');
$cacheDir = rtrim($cacheDir, '/');
$cacheDir .= '/'.self::SITEMAP_CACHE_DIR.'/';
return $cacheDir;
}
/**
* Check if current user has ADMIN role.
*
* @return bool
*/
protected function checkAdmin()
{
return $this->getSecurityContext()->hasAdminUser();
}
/**
* Check if a lang is used.
*
* @param $lang The lang code. e.g.: fr
*
* @return bool true if the language is used, otherwise false
*/
private function checkLang($lang)
{
// load locals
$lang = LangQuery::create()
->findOneByCode($lang);
return null !== $lang;
}
}