<?php declare(strict_types=1);
namespace Shopware\Storefront\Controller;
use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
use Shopware\Core\Checkout\Customer\CustomerEntity;
use Shopware\Core\Checkout\Customer\Exception\CustomerAlreadyConfirmedException;
use Shopware\Core\Checkout\Customer\Exception\CustomerNotFoundByHashException;
use Shopware\Core\Checkout\Customer\SalesChannel\AbstractRegisterConfirmRoute;
use Shopware\Core\Checkout\Customer\SalesChannel\AbstractRegisterRoute;
use Shopware\Core\Content\Newsletter\Exception\SalesChannelDomainNotFoundException;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\Feature;
use Shopware\Core\Framework\Log\Package;
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
use Shopware\Core\Framework\Routing\Annotation\Since;
use Shopware\Core\Framework\Routing\Exception\MissingRequestParameterException;
use Shopware\Core\Framework\Validation\DataBag\DataBag;
use Shopware\Core\Framework\Validation\DataBag\QueryDataBag;
use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
use Shopware\Core\Framework\Validation\DataValidationDefinition;
use Shopware\Core\Framework\Validation\Exception\ConstraintViolationException;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Shopware\Storefront\Framework\AffiliateTracking\AffiliateTrackingListener;
use Shopware\Storefront\Framework\Captcha\Annotation\Captcha;
use Shopware\Storefront\Framework\Routing\Annotation\NoStore;
use Shopware\Storefront\Framework\Routing\RequestTransformer;
use Shopware\Storefront\Page\Account\CustomerGroupRegistration\AbstractCustomerGroupRegistrationPageLoader;
use Shopware\Storefront\Page\Account\CustomerGroupRegistration\CustomerGroupRegistrationPageLoadedHook;
use Shopware\Storefront\Page\Account\Login\AccountLoginPageLoader;
use Shopware\Storefront\Page\Account\Register\AccountRegisterPageLoadedHook;
use Shopware\Storefront\Page\Checkout\Register\CheckoutRegisterPageLoadedHook;
use Shopware\Storefront\Page\Checkout\Register\CheckoutRegisterPageLoader;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Constraints\EqualTo;
use Symfony\Component\Validator\Constraints\NotBlank;
/**
* @Route(defaults={"_routeScope"={"storefront"}})
*
* @deprecated tag:v6.5.0 - reason:becomes-internal - Will be internal
*/
#[Package('customer-order')]
class RegisterController extends StorefrontController
{
private AccountLoginPageLoader $loginPageLoader;
private CartService $cartService;
private CheckoutRegisterPageLoader $registerPageLoader;
private SystemConfigService $systemConfigService;
private EntityRepositoryInterface $customerRepository;
private AbstractCustomerGroupRegistrationPageLoader $customerGroupRegistrationPageLoader;
private AbstractRegisterRoute $registerRoute;
private AbstractRegisterConfirmRoute $registerConfirmRoute;
private EntityRepositoryInterface $domainRepository;
/**
* @internal
*/
public function __construct(
AccountLoginPageLoader $loginPageLoader,
AbstractRegisterRoute $registerRoute,
AbstractRegisterConfirmRoute $registerConfirmRoute,
CartService $cartService,
CheckoutRegisterPageLoader $registerPageLoader,
SystemConfigService $systemConfigService,
EntityRepositoryInterface $customerRepository,
AbstractCustomerGroupRegistrationPageLoader $customerGroupRegistrationPageLoader,
EntityRepositoryInterface $domainRepository
) {
$this->loginPageLoader = $loginPageLoader;
$this->cartService = $cartService;
$this->registerPageLoader = $registerPageLoader;
$this->systemConfigService = $systemConfigService;
$this->customerRepository = $customerRepository;
$this->customerGroupRegistrationPageLoader = $customerGroupRegistrationPageLoader;
$this->registerRoute = $registerRoute;
$this->registerConfirmRoute = $registerConfirmRoute;
$this->domainRepository = $domainRepository;
}
/**
* @Since("6.0.0.0")
* @Route("/account/register", name="frontend.account.register.page", methods={"GET"})
* @NoStore
*/
public function accountRegisterPage(Request $request, RequestDataBag $data, SalesChannelContext $context): Response
{
if ($context->getCustomer() && $context->getCustomer()->getGuest()) {
return $this->redirectToRoute('frontend.account.logout.page');
}
if ($context->getCustomer()) {
return $this->redirectToRoute('frontend.account.home.page');
}
$redirect = $request->query->get('redirectTo', 'frontend.account.home.page');
$page = $this->loginPageLoader->load($request, $context);
$this->hook(new AccountRegisterPageLoadedHook($page, $context));
return $this->renderStorefront('@Storefront/storefront/page/account/register/index.html.twig', [
'redirectTo' => $redirect,
'redirectParameters' => $request->get('redirectParameters', json_encode([])),
'page' => $page,
'data' => $data,
]);
}
/**
* @Since("6.3.1.0")
* @Route("/customer-group-registration/{customerGroupId}", name="frontend.account.customer-group-registration.page", methods={"GET"})
* @NoStore
*/
public function customerGroupRegistration(string $customerGroupId, Request $request, RequestDataBag $data, SalesChannelContext $context): Response
{
if ($context->getCustomer() && $context->getCustomer()->getGuest()) {
return $this->redirectToRoute('frontend.account.logout.page');
}
if ($context->getCustomer()) {
return $this->redirectToRoute('frontend.account.home.page');
}
$redirect = $request->query->get('redirectTo', 'frontend.account.home.page');
$page = $this->customerGroupRegistrationPageLoader->load($request, $context);
if ($page->getGroup()->getTranslation('registrationOnlyCompanyRegistration')) {
$data->set('accountType', CustomerEntity::ACCOUNT_TYPE_BUSINESS);
}
$this->hook(new CustomerGroupRegistrationPageLoadedHook($page, $context));
return $this->renderStorefront('@Storefront/storefront/page/account/customer-group-register/index.html.twig', [
'redirectTo' => $redirect,
'redirectParameters' => $request->get('redirectParameters', json_encode([])),
'errorParameters' => json_encode(['customerGroupId' => $customerGroupId]),
'page' => $page,
'data' => $data,
]);
}
/**
* @Since("6.0.0.0")
* @Route("/checkout/register", name="frontend.checkout.register.page", options={"seo"="false"}, methods={"GET"})
* @NoStore
*/
public function checkoutRegisterPage(Request $request, RequestDataBag $data, SalesChannelContext $context): Response
{
/** @var string $redirect */
$redirect = $request->get('redirectTo', 'frontend.checkout.confirm.page');
if ($context->getCustomer()) {
return $this->redirectToRoute($redirect);
}
if ($this->cartService->getCart($context->getToken(), $context)->getLineItems()->count() === 0) {
return $this->redirectToRoute('frontend.checkout.cart.page');
}
$page = $this->registerPageLoader->load($request, $context);
$this->hook(new CheckoutRegisterPageLoadedHook($page, $context));
return $this->renderStorefront(
'@Storefront/storefront/page/checkout/address/index.html.twig',
['redirectTo' => $redirect, 'page' => $page, 'data' => $data]
);
}
/**
* @Since("6.0.0.0")
* @Route("/account/register", name="frontend.account.register.save", methods={"POST"}, defaults={"_captcha"=true})
*/
public function register(Request $request, RequestDataBag $data, SalesChannelContext $context): Response
{
if ($context->getCustomer()) {
return $this->redirectToRoute('frontend.account.home.page');
}
try {
if (!$data->has('differentShippingAddress')) {
$data->remove('shippingAddress');
}
$data->set('storefrontUrl', $this->getConfirmUrl($context, $request));
$data = $this->prepareAffiliateTracking($data, $request->getSession());
if (Feature::isActive('FEATURE_NEXT_16236')) {
if ($data->getBoolean('createCustomerAccount')) {
$data->set('guest', false);
} else {
$data->set('guest', true);
}
} else {
if ($data->has('guest')) {
$data->set('guest', $data->has('guest'));
}
}
$this->registerRoute->register(
$data->toRequestDataBag(),
$context,
false,
$this->getAdditionalRegisterValidationDefinitions($data, $context)
);
} catch (ConstraintViolationException $formViolations) {
if (!$request->request->has('errorRoute')) {
throw new MissingRequestParameterException('errorRoute');
}
$params = $this->decodeParam($request, 'errorParameters');
// this is to show the correct form because we have different usecases (account/register||checkout/register)
return $this->forwardToRoute($request->get('errorRoute'), ['formViolations' => $formViolations], $params);
}
if ($this->isDoubleOptIn($data, $context)) {
return $this->redirectToRoute('frontend.account.register.page');
}
return $this->createActionResponse($request);
}
/**
* @Since("6.1.0.0")
* @Route("/registration/confirm", name="frontend.account.register.mail", methods={"GET"})
*/
public function confirmRegistration(SalesChannelContext $context, QueryDataBag $queryDataBag): Response
{
try {
$customerId = $this->registerConfirmRoute
->confirm($queryDataBag->toRequestDataBag(), $context)
->getCustomer()
->getId();
} catch (CustomerNotFoundByHashException | CustomerAlreadyConfirmedException | ConstraintViolationException $exception) {
$this->addFlash(self::DANGER, $this->trans('account.confirmationIsAlreadyDone'));
return $this->redirectToRoute('frontend.account.register.page');
}
/** @var CustomerEntity $customer */
$customer = $this->customerRepository->search(new Criteria([$customerId]), $context->getContext())->first();
if ($customer->getGuest()) {
$this->addFlash(self::SUCCESS, $this->trans('account.doubleOptInMailConfirmationSuccessfully'));
return $this->redirectToRoute('frontend.checkout.confirm.page');
}
$this->addFlash(self::SUCCESS, $this->trans('account.doubleOptInRegistrationSuccessfully'));
if ($redirectTo = $queryDataBag->get('redirectTo')) {
return $this->redirectToRoute($redirectTo);
}
return $this->redirectToRoute('frontend.account.home.page');
}
private function isDoubleOptIn(DataBag $data, SalesChannelContext $context): bool
{
$creatueCustomerAccount = $data->getBoolean('createCustomerAccount');
if (Feature::isActive('FEATURE_NEXT_16236')) {
$configKey = $creatueCustomerAccount
? 'core.loginRegistration.doubleOptInRegistration'
: 'core.loginRegistration.doubleOptInGuestOrder';
} else {
$configKey = $data->has('guest')
? 'core.loginRegistration.doubleOptInGuestOrder'
: 'core.loginRegistration.doubleOptInRegistration';
}
$doubleOptInRequired = $this->systemConfigService
->get($configKey, $context->getSalesChannel()->getId());
if (!$doubleOptInRequired) {
return false;
}
if (Feature::isActive('FEATURE_NEXT_16236')) {
if ($creatueCustomerAccount) {
$this->addFlash(self::SUCCESS, $this->trans('account.optInRegistrationAlert'));
return true;
}
$this->addFlash(self::SUCCESS, $this->trans('account.optInGuestAlert'));
return true;
}
if ($data->has('guest')) {
$this->addFlash(self::SUCCESS, $this->trans('account.optInGuestAlert'));
return true;
}
$this->addFlash(self::SUCCESS, $this->trans('account.optInRegistrationAlert'));
return true;
}
private function getAdditionalRegisterValidationDefinitions(DataBag $data, SalesChannelContext $context): DataValidationDefinition
{
$definition = new DataValidationDefinition('storefront.confirmation');
$definition->add('salutationId', new NotBlank());
if ($this->systemConfigService->get('core.loginRegistration.requireEmailConfirmation', $context->getSalesChannel()->getId())) {
$definition->add('emailConfirmation', new NotBlank(), new EqualTo([
'value' => $data->get('email'),
]));
}
if ($data->has('guest')) {
return $definition;
}
if ($this->systemConfigService->get('core.loginRegistration.requirePasswordConfirmation', $context->getSalesChannel()->getId())) {
$definition->add('passwordConfirmation', new NotBlank(), new EqualTo([
'value' => $data->get('password'),
]));
}
return $definition;
}
private function prepareAffiliateTracking(RequestDataBag $data, SessionInterface $session): DataBag
{
$affiliateCode = $session->get(AffiliateTrackingListener::AFFILIATE_CODE_KEY);
$campaignCode = $session->get(AffiliateTrackingListener::CAMPAIGN_CODE_KEY);
if ($affiliateCode !== null && $campaignCode !== null) {
$data->add([
AffiliateTrackingListener::AFFILIATE_CODE_KEY => $affiliateCode,
AffiliateTrackingListener::CAMPAIGN_CODE_KEY => $campaignCode,
]);
}
return $data;
}
private function getConfirmUrl(SalesChannelContext $context, Request $request): string
{
/** @var string $domainUrl */
$domainUrl = $this->systemConfigService
->get('core.loginRegistration.doubleOptInDomain', $context->getSalesChannel()->getId());
if ($domainUrl) {
return $domainUrl;
}
$domainUrl = $request->attributes->get(RequestTransformer::STOREFRONT_URL);
if ($domainUrl) {
return $domainUrl;
}
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('salesChannelId', $context->getSalesChannel()->getId()));
$criteria->setLimit(1);
$domain = $this->domainRepository
->search($criteria, $context->getContext())
->first();
if (!$domain) {
throw new SalesChannelDomainNotFoundException($context->getSalesChannel());
}
return $domain->getUrl();
}
}