- <?php declare(strict_types=1);
- namespace Shopware\Storefront\Framework\Routing;
- use Shopware\Core\Checkout\Cart\Exception\CustomerNotLoggedInException;
- use Shopware\Core\Checkout\Customer\Event\CustomerLoginEvent;
- use Shopware\Core\Checkout\Customer\Event\CustomerLogoutEvent;
- use Shopware\Core\Content\Seo\HreflangLoaderInterface;
- use Shopware\Core\Content\Seo\HreflangLoaderParameter;
- use Shopware\Core\Framework\App\ActiveAppsLoader;
- use Shopware\Core\Framework\App\Exception\AppUrlChangeDetectedException;
- use Shopware\Core\Framework\App\ShopId\ShopIdProvider;
- use Shopware\Core\Framework\Event\BeforeSendResponseEvent;
- use Shopware\Core\Framework\Feature;
- use Shopware\Core\Framework\Log\Package;
- use Shopware\Core\Framework\Routing\Annotation\RouteScope;
- use Shopware\Core\Framework\Routing\Event\SalesChannelContextResolvedEvent;
- use Shopware\Core\Framework\Routing\KernelListenerPriorities;
- use Shopware\Core\Framework\Util\Random;
- use Shopware\Core\PlatformRequest;
- use Shopware\Core\SalesChannelRequest;
- use Shopware\Core\System\SalesChannel\SalesChannelContext;
- use Shopware\Core\System\SystemConfig\SystemConfigService;
- use Shopware\Storefront\Event\StorefrontRenderEvent;
- use Shopware\Storefront\Framework\Csrf\CsrfPlaceholderHandler;
- use Shopware\Storefront\Framework\Routing\NotFound\NotFoundSubscriber;
- use Shopware\Storefront\Theme\StorefrontPluginRegistryInterface;
- use Symfony\Component\EventDispatcher\EventSubscriberInterface;
- use Symfony\Component\HttpFoundation\RedirectResponse;
- use Symfony\Component\HttpFoundation\RequestStack;
- use Symfony\Component\HttpFoundation\Session\SessionInterface;
- use Symfony\Component\HttpKernel\Event\ControllerEvent;
- use Symfony\Component\HttpKernel\Event\ExceptionEvent;
- use Symfony\Component\HttpKernel\Event\RequestEvent;
- use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
- use Symfony\Component\HttpKernel\KernelEvents;
- use Symfony\Component\Routing\RouterInterface;
- /**
-  * @deprecated tag:v6.5.0 - reason:becomes-internal - EventSubscribers will become internal in v6.5.0
-  */
- #[Package('storefront')]
- class StorefrontSubscriber implements EventSubscriberInterface
- {
-     private RequestStack $requestStack;
-     private RouterInterface $router;
-     private CsrfPlaceholderHandler $csrfPlaceholderHandler;
-     private MaintenanceModeResolver $maintenanceModeResolver;
-     private HreflangLoaderInterface $hreflangLoader;
-     private ShopIdProvider $shopIdProvider;
-     private ActiveAppsLoader $activeAppsLoader;
-     private SystemConfigService $systemConfigService;
-     private StorefrontPluginRegistryInterface $themeRegistry;
-     private NotFoundSubscriber $notFoundSubscriber;
-     /**
-      * @internal
-      */
-     public function __construct(
-         RequestStack $requestStack,
-         RouterInterface $router,
-         CsrfPlaceholderHandler $csrfPlaceholderHandler,
-         HreflangLoaderInterface $hreflangLoader,
-         MaintenanceModeResolver $maintenanceModeResolver,
-         ShopIdProvider $shopIdProvider,
-         ActiveAppsLoader $activeAppsLoader,
-         SystemConfigService $systemConfigService,
-         StorefrontPluginRegistryInterface $themeRegistry,
-         NotFoundSubscriber $notFoundSubscriber
-     ) {
-         $this->requestStack = $requestStack;
-         $this->router = $router;
-         $this->csrfPlaceholderHandler = $csrfPlaceholderHandler;
-         $this->maintenanceModeResolver = $maintenanceModeResolver;
-         $this->hreflangLoader = $hreflangLoader;
-         $this->shopIdProvider = $shopIdProvider;
-         $this->activeAppsLoader = $activeAppsLoader;
-         $this->systemConfigService = $systemConfigService;
-         $this->themeRegistry = $themeRegistry;
-         $this->notFoundSubscriber = $notFoundSubscriber;
-     }
-     public static function getSubscribedEvents(): array
-     {
-         if (Feature::isActive('v6.5.0.0')) {
-             return [
-                 KernelEvents::REQUEST => [
-                     ['startSession', 40],
-                     ['maintenanceResolver'],
-                 ],
-                 KernelEvents::EXCEPTION => [
-                     ['customerNotLoggedInHandler'],
-                     ['maintenanceResolver'],
-                 ],
-                 KernelEvents::CONTROLLER => [
-                     ['preventPageLoadingFromXmlHttpRequest', KernelListenerPriorities::KERNEL_CONTROLLER_EVENT_SCOPE_VALIDATE],
-                 ],
-                 CustomerLoginEvent::class => [
-                     'updateSessionAfterLogin',
-                 ],
-                 CustomerLogoutEvent::class => [
-                     'updateSessionAfterLogout',
-                 ],
-                 BeforeSendResponseEvent::class => [
-                     ['setCanonicalUrl'],
-                 ],
-                 StorefrontRenderEvent::class => [
-                     ['addHreflang'],
-                     ['addShopIdParameter'],
-                     ['addIconSetConfig'],
-                 ],
-                 SalesChannelContextResolvedEvent::class => [
-                     ['replaceContextToken'],
-                 ],
-             ];
-         }
-         return [
-             KernelEvents::REQUEST => [
-                 ['startSession', 40],
-                 ['maintenanceResolver'],
-             ],
-             KernelEvents::EXCEPTION => [
-                 ['showHtmlExceptionResponse', -100],
-                 ['customerNotLoggedInHandler'],
-                 ['maintenanceResolver'],
-             ],
-             KernelEvents::CONTROLLER => [
-                 ['preventPageLoadingFromXmlHttpRequest', KernelListenerPriorities::KERNEL_CONTROLLER_EVENT_SCOPE_VALIDATE],
-             ],
-             CustomerLoginEvent::class => [
-                 'updateSessionAfterLogin',
-             ],
-             CustomerLogoutEvent::class => [
-                 'updateSessionAfterLogout',
-             ],
-             BeforeSendResponseEvent::class => [
-                 ['replaceCsrfToken'],
-                 ['setCanonicalUrl'],
-             ],
-             StorefrontRenderEvent::class => [
-                 ['addHreflang'],
-                 ['addShopIdParameter'],
-                 ['addIconSetConfig'],
-             ],
-             SalesChannelContextResolvedEvent::class => [
-                 ['replaceContextToken'],
-             ],
-         ];
-     }
-     public function startSession(): void
-     {
-         $master = $this->requestStack->getMainRequest();
-         if (!$master) {
-             return;
-         }
-         if (!$master->attributes->get(SalesChannelRequest::ATTRIBUTE_IS_SALES_CHANNEL_REQUEST)) {
-             return;
-         }
-         if (!$master->hasSession()) {
-             return;
-         }
-         $session = $master->getSession();
-         if (!$session->isStarted()) {
-             $session->setName('session-');
-             $session->start();
-             $session->set('sessionId', $session->getId());
-         }
-         $salesChannelId = $master->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID);
-         if ($salesChannelId === null) {
-             /** @var SalesChannelContext|null $salesChannelContext */
-             $salesChannelContext = $master->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
-             if ($salesChannelContext !== null) {
-                 $salesChannelId = $salesChannelContext->getSalesChannel()->getId();
-             }
-         }
-         if ($this->shouldRenewToken($session, $salesChannelId)) {
-             $token = Random::getAlphanumericString(32);
-             $session->set(PlatformRequest::HEADER_CONTEXT_TOKEN, $token);
-             $session->set(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID, $salesChannelId);
-         }
-         $master->headers->set(
-             PlatformRequest::HEADER_CONTEXT_TOKEN,
-             $session->get(PlatformRequest::HEADER_CONTEXT_TOKEN)
-         );
-     }
-     public function updateSessionAfterLogin(CustomerLoginEvent $event): void
-     {
-         $token = $event->getContextToken();
-         $this->updateSession($token);
-     }
-     public function updateSessionAfterLogout(): void
-     {
-         $newToken = Random::getAlphanumericString(32);
-         $this->updateSession($newToken, true);
-     }
-     public function updateSession(string $token, bool $destroyOldSession = false): void
-     {
-         $master = $this->requestStack->getMainRequest();
-         if (!$master) {
-             return;
-         }
-         if (!$master->attributes->get(SalesChannelRequest::ATTRIBUTE_IS_SALES_CHANNEL_REQUEST)) {
-             return;
-         }
-         if (!$master->hasSession()) {
-             return;
-         }
-         $session = $master->getSession();
-         $session->migrate($destroyOldSession);
-         $session->set('sessionId', $session->getId());
-         $session->set(PlatformRequest::HEADER_CONTEXT_TOKEN, $token);
-         $master->headers->set(PlatformRequest::HEADER_CONTEXT_TOKEN, $token);
-     }
-     /**
-      * @deprecated tag:v6.5.0 - reason:remove-subscriber - Use `NotFoundSubscriber::onError` instead
-      */
-     public function showHtmlExceptionResponse(ExceptionEvent $event): void
-     {
-         $this->notFoundSubscriber->onError($event);
-     }
-     public function customerNotLoggedInHandler(ExceptionEvent $event): void
-     {
-         if (!$event->getRequest()->attributes->has(SalesChannelRequest::ATTRIBUTE_IS_SALES_CHANNEL_REQUEST)) {
-             return;
-         }
-         if (!$event->getThrowable() instanceof CustomerNotLoggedInException) {
-             return;
-         }
-         $request = $event->getRequest();
-         $parameters = [
-             'redirectTo' => $request->attributes->get('_route'),
-             'redirectParameters' => json_encode($request->attributes->get('_route_params')),
-         ];
-         $redirectResponse = new RedirectResponse($this->router->generate('frontend.account.login.page', $parameters));
-         $event->setResponse($redirectResponse);
-     }
-     public function maintenanceResolver(RequestEvent $event): void
-     {
-         if ($this->maintenanceModeResolver->shouldRedirect($event->getRequest())) {
-             $event->setResponse(
-                 new RedirectResponse(
-                     $this->router->generate('frontend.maintenance.page'),
-                     RedirectResponse::HTTP_TEMPORARY_REDIRECT
-                 )
-             );
-         }
-     }
-     public function preventPageLoadingFromXmlHttpRequest(ControllerEvent $event): void
-     {
-         if (!$event->getRequest()->isXmlHttpRequest()) {
-             return;
-         }
-         /** @var RouteScope|list<string> $scope */
-         $scope = $event->getRequest()->attributes->get(PlatformRequest::ATTRIBUTE_ROUTE_SCOPE, []);
-         if ($scope instanceof RouteScope) {
-             $scope = $scope->getScopes();
-         }
-         if (!\in_array(StorefrontRouteScope::ID, $scope, true)) {
-             return;
-         }
-         $controller = $event->getController();
-         // happens if Controller is a closure
-         if (!\is_array($controller)) {
-             return;
-         }
-         $isAllowed = $event->getRequest()->attributes->getBoolean('XmlHttpRequest', false);
-         if ($isAllowed) {
-             return;
-         }
-         throw new AccessDeniedHttpException('PageController can\'t be requested via XmlHttpRequest.');
-     }
-     // used to switch session token - when the context token expired
-     public function replaceContextToken(SalesChannelContextResolvedEvent $event): void
-     {
-         $context = $event->getSalesChannelContext();
-         // only update session if token expired and switched
-         if ($event->getUsedToken() === $context->getToken()) {
-             return;
-         }
-         $this->updateSession($context->getToken());
-     }
-     public function setCanonicalUrl(BeforeSendResponseEvent $event): void
-     {
-         if (!$event->getResponse()->isSuccessful()) {
-             return;
-         }
-         if ($canonical = $event->getRequest()->attributes->get(SalesChannelRequest::ATTRIBUTE_CANONICAL_LINK)) {
-             $canonical = sprintf('<%s>; rel="canonical"', $canonical);
-             $event->getResponse()->headers->set('Link', $canonical);
-         }
-     }
-     /**
-      * @deprecated tag:v6.5.0 - replaceCsrfToken method will be removed as the csrf system will be removed in favor for the samesite approach
-      */
-     public function replaceCsrfToken(BeforeSendResponseEvent $event): void
-     {
-         if (Feature::isActive('v6.5.0.0')) {
-             return;
-         }
-         Feature::triggerDeprecationOrThrow(
-             'v6.5.0.0',
-             Feature::deprecatedMethodMessage(__CLASS__, __METHOD__, 'v6.5.0.0')
-         );
-         $event->setResponse(
-             $this->csrfPlaceholderHandler->replaceCsrfToken($event->getResponse(), $event->getRequest())
-         );
-     }
-     public function addHreflang(StorefrontRenderEvent $event): void
-     {
-         $request = $event->getRequest();
-         $route = $request->attributes->get('_route');
-         if ($route === null) {
-             return;
-         }
-         $routeParams = $request->attributes->get('_route_params', []);
-         $salesChannelContext = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
-         $parameter = new HreflangLoaderParameter($route, $routeParams, $salesChannelContext);
-         $event->setParameter('hrefLang', $this->hreflangLoader->load($parameter));
-     }
-     public function addShopIdParameter(StorefrontRenderEvent $event): void
-     {
-         if (!$this->activeAppsLoader->getActiveApps()) {
-             return;
-         }
-         try {
-             $shopId = $this->shopIdProvider->getShopId();
-         } catch (AppUrlChangeDetectedException $e) {
-             return;
-         }
-         $event->setParameter('appShopId', $shopId);
-     }
-     public function addIconSetConfig(StorefrontRenderEvent $event): void
-     {
-         $request = $event->getRequest();
-         // get name if theme is not inherited
-         $theme = $request->attributes->get(SalesChannelRequest::ATTRIBUTE_THEME_NAME);
-         if (!$theme) {
-             // get theme name from base theme because for inherited themes the name is always null
-             $theme = $request->attributes->get(SalesChannelRequest::ATTRIBUTE_THEME_BASE_NAME);
-         }
-         if (!$theme) {
-             return;
-         }
-         $themeConfig = $this->themeRegistry->getConfigurations()->getByTechnicalName($theme);
-         if (!$themeConfig) {
-             return;
-         }
-         $iconConfig = [];
-         foreach ($themeConfig->getIconSets() as $pack => $path) {
-             $iconConfig[$pack] = [
-                 'path' => $path,
-                 'namespace' => $theme,
-             ];
-         }
-         $event->setParameter('themeIconConfig', $iconConfig);
-     }
-     private function shouldRenewToken(SessionInterface $session, ?string $salesChannelId = null): bool
-     {
-         if (!$session->has(PlatformRequest::HEADER_CONTEXT_TOKEN) || $salesChannelId === null) {
-             return true;
-         }
-         if ($this->systemConfigService->get('core.systemWideLoginRegistration.isCustomerBoundToSalesChannel')) {
-             return $session->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID) !== $salesChannelId;
-         }
-         return false;
-     }
- }
-