<?php
declare(strict_types=1);
namespace NetInventors\NetiNextAccessManager\Subscriber;
use NetInventors\NetiNextAccessManager\Components\StringHelper;
use NetInventors\NetiNextAccessManager\Events\Subscriber\ExtendControllerWhitelist;
use NetInventors\NetiNextAccessManager\Events\Subscriber\ExtendTemplatesEvent;
use NetInventors\NetiNextAccessManager\Service\PluginConfig;
use NetInventors\NetiNextAccessManager\Service\RouteScopeHandler\RouteScopeHandlerInterface;
use Shopware\Core\Checkout\Cart\Event\AfterLineItemAddedEvent;
use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
use Shopware\Core\Checkout\Customer\CustomerEntity;
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
use Shopware\Core\Framework\Routing\Event\SalesChannelContextResolvedEvent;
use Shopware\Core\System\SalesChannel\SalesChannelEntity;
use Shopware\Storefront\Controller\AuthController;
use Shopware\Storefront\Controller\CartLineItemController;
use Shopware\Storefront\Controller\CheckoutController;
use Shopware\Storefront\Controller\CmsController;
use Shopware\Storefront\Controller\ContextController;
use Shopware\Storefront\Controller\CookieController;
use Shopware\Storefront\Controller\CountryStateController;
use Shopware\Storefront\Controller\FormController;
use Shopware\Storefront\Controller\RegisterController;
use Shopware\Storefront\Controller\SearchController;
use Symfony\Bundle\FrameworkBundle\Controller\TemplateController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\RouterInterface;
class StorefrontSubscriber implements EventSubscriberInterface
{
/**
* @var CustomerEntity|null
*/
private $customer;
/**
* @var ?SalesChannelEntity
*/
private $salesChannel;
/**
* @var PluginConfig
*/
private $pluginConfig;
/**
* @var RouterInterface
*/
private $router;
/**
* @var TemplateController
*/
private $templateController;
/**
* @var EventDispatcherInterface
*/
private $eventDispatcher;
/**
* @var CartService
*/
private $cartService;
public function __construct(
PluginConfig $pluginConfig,
RouterInterface $router,
TemplateController $templateController,
EventDispatcherInterface $eventDispatcher,
CartService $cartService
) {
$this->pluginConfig = $pluginConfig;
$this->router = $router;
$this->templateController = $templateController;
$this->eventDispatcher = $eventDispatcher;
$this->cartService = $cartService;
}
/**
* @return array|string[]
*/
public static function getSubscribedEvents(): array
{
return [
SalesChannelContextResolvedEvent::class => 'onSalesChannelResolved',
KernelEvents::RESPONSE => 'onResponse',
AfterLineItemAddedEvent::class => 'onItemAdded',
];
}
/**
* @param SalesChannelContextResolvedEvent $event
*/
public function onSalesChannelResolved(SalesChannelContextResolvedEvent $event): void
{
if (!$this->pluginConfig->isActive()) {
return;
}
$this->salesChannel = $event->getSalesChannelContext()->getSalesChannel();
$this->customer = $event->getSalesChannelContext()->getCustomer();
}
/**
* @param ResponseEvent $event
*/
public function onResponse(ResponseEvent $event): void
{
if (!$this->pluginConfig->isActive() || (null !== $this->salesChannel && $this->salesChannel->isMaintenance())) {
return;
}
$request = $event->getRequest();
$response = null;
if ($this->isShopHidden($request)) {
$message['type'] = 'danger';
$message['text'] = 'login-first';
if ($request->isXmlHttpRequest()) {
/**
* @psalm-suppress DeprecatedClass
* These classes will be internal since SW 6.5.0.0.
*/
$templates = [
CartLineItemController::class => '@NetiNextAccessManager/storefront/access-manager/blocked/search.html.twig',
SearchController::class => '@NetiNextAccessManager/storefront/access-manager/blocked/search.html.twig',
CheckoutController::class => '@NetiNextAccessManager/storefront/access-manager/blocked/cart-offcanvas.html.twig',
];
$extendTemplatesEvent = new ExtendTemplatesEvent($this, $templates);
$this->eventDispatcher->dispatch($extendTemplatesEvent, ExtendTemplatesEvent::NAME);
$currentController = $request->attributes->get('_controller');
$currentController = substr($currentController, 0, strpos($currentController, '::'));
if (isset($templates[$currentController]) && $request->attributes->get('_route') !== 'frontend.checkout.info') {
$response = $this->templateController->templateAction($templates[$currentController]);
}
} elseif ('' !== $this->pluginConfig->getRedirectCategory()) {
$response = new RedirectResponse(
$this->router->generate(
'frontend.navigation.page',
[ 'navigationId' => $this->pluginConfig->getRedirectCategory() ]
)
);
} else {
$response = new RedirectResponse(
$this->router->generate('frontend.account.login.page', [ 'amMessage' => $message ])
);
}
if (null !== $response) {
$event->setResponse($response);
}
}
}
/**
* @param Request $request
*
* @return bool
*/
public function isShopHidden(Request $request): bool
{
/**
* @var RouteScope|array
*
* RouteScope will be removed in SW 6.5 and this Subscriber will be adjusted
* It is not possible to only rely on array because custom Controller can still use RouteScope
*
* @psalm-suppress DeprecatedClass
*/
$routeScope = $request->attributes->get('_routeScope');
if (null === $routeScope) {
return false;
}
if ($routeScope instanceof RouteScope) {
if (
$routeScope->hasScope('administration')
|| $routeScope->hasScope('api')
|| $routeScope->hasScope('store-api')
) {
return false;
}
}
if (\is_array($routeScope)) {
if (
\in_array('administration', $routeScope, true)
|| \in_array('api', $routeScope, true)
|| \in_array('store-api', $routeScope, true)
) {
return false;
}
}
if (null !== $this->customer || false === $this->pluginConfig->isHideShop() || !$this->salesChannel instanceof SalesChannelEntity) {
return false;
}
$navigationId = $request->attributes->get('navigationId');
if (
\in_array($navigationId, $this->pluginConfig->getAllowedCategories(), true)
|| $navigationId === $this->pluginConfig->getRedirectCategory()
|| ($request->getRequestUri() === '/'
&& \in_array(
$this->salesChannel->getNavigationCategoryId(),
$this->pluginConfig->getAllowedCategories(),
true
))
|| \in_array($request->getRequestUri(), $this->pluginConfig->getWhitelistedPaths())
) {
return false;
}
foreach ($this->pluginConfig->getWhitelistedPaths() as $path) {
/** @var ?string $originRequestUri */
$originRequestUri = $request->attributes->get('sw-original-request-uri');
if (
0 < StringHelper::stringMatchWithWildcard($request->getRequestUri(), $path)
|| (
\is_string($originRequestUri)
&& 0 < StringHelper::stringMatchWithWildcard($originRequestUri, $path)
)
) {
return false;
}
}
/**
* @psalm-suppress DeprecatedClass
* These classes will be internal since SW 6.5.0.0.
*/
$accessList = array_merge([
AuthController::class,
ContextController::class,
CmsController::class,
FormController::class,
CountryStateController::class,
RegisterController::class,
CookieController::class,
], $this->pluginConfig->getWhitelistedControllers());
$extendWhitelistControllerEvent = new ExtendControllerWhitelist($this, $accessList);
$this->eventDispatcher->dispatch($extendWhitelistControllerEvent, ExtendControllerWhitelist::NAME);
$currentController = $request->attributes->get('_controller');
if (null === $currentController) {
return false;
}
foreach ($accessList as $controller) {
if (0 === strpos($currentController, "{$controller}::")) {
return false;
}
if (0 < StringHelper::stringMatchWithWildcard($currentController, $controller)) {
return false;
}
}
return true;
}
/**
* @param AfterLineItemAddedEvent $event
*/
public function onItemAdded(AfterLineItemAddedEvent $event): void
{
if (
!$this->pluginConfig->isHideShop()
|| !$this->pluginConfig->isActive()
|| $event->getSalesChannelContext()->getCustomer() !== null
) {
return;
}
$this->cartService->deleteCart($event->getSalesChannelContext());
}
}