* * 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 */ 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 */); } }