custom/plugins/b2bsellerscore/src/Components/B2bPlatform/Subscriber/ResponseSubscriber.php line 104

Open in your IDE?
  1. <?php
  2. namespace B2bSellersCore\Components\B2bPlatform\Subscriber;
  3. use B2bSellersCore\Components\B2bPlatform\Events\BeforeB2bRedirectAccountPageEvent;
  4. use B2bSellersCore\Components\B2bPlatform\Events\BeforeB2bRedirectEmployeeCreationPageEvent;
  5. use B2bSellersCore\Components\B2bPlatform\Events\BeforeB2bRedirectEmployeeFirstAdminEvent;
  6. use B2bSellersCore\Components\B2bPlatform\Events\BeforeB2bRedirectEvent;
  7. use B2bSellersCore\Components\B2bPlatform\Events\BeforeB2bRedirectSalesRepresentativeEvent;
  8. use B2bSellersCore\Components\B2bPlatform\Traits\B2bContextTrait;
  9. use B2bSellersCore\Components\Employee\Aggregate\EmployeeCustomer\EmployeeCustomerCollection;
  10. use B2bSellersCore\Storefront\Page\B2bCreateEmployee\B2bCreateEmployeePage;
  11. use B2bSellersCore\Storefront\Page\B2bPlatform\B2bPlatformPage;
  12. use B2bSellersCore\Storefront\Page\B2bSelectEmployeeAdministrator\B2bSelectEmployeeAdministratorPage;
  13. use Shopware\Core\Content\Cms\CmsPageEntity;
  14. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  15. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  16. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  17. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  18. use Shopware\Core\System\SystemConfig\SystemConfigService;
  19. use Shopware\Storefront\Framework\Routing\StorefrontResponse;
  20. use Shopware\Storefront\Page\Account\Order\AccountOrderPage;
  21. use Shopware\Storefront\Page\Account\Overview\AccountOverviewPage;
  22. use Shopware\Storefront\Page\Account\Profile\AccountProfilePage;
  23. use Shopware\Storefront\Page\Address\Detail\AddressDetailPage;
  24. use Shopware\Storefront\Page\Address\Listing\AddressListingPage;
  25. use Shopware\Storefront\Page\Checkout\Offcanvas\OffcanvasCartPage;
  26. use Shopware\Storefront\Page\Page;
  27. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  28. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  29. use Symfony\Component\HttpFoundation\RedirectResponse;
  30. use Symfony\Component\HttpFoundation\Request;
  31. use Symfony\Component\HttpKernel\Event\ResponseEvent;
  32. use Symfony\Component\HttpKernel\HttpKernelInterface;
  33. use Symfony\Component\HttpKernel\KernelEvents;
  34. use Symfony\Component\Routing\RouterInterface;
  35. class ResponseSubscriber implements EventSubscriberInterface
  36. {
  37.     use B2bContextTrait;
  38.     public const SALES_REPRESENTATIVE_PAGE_WHITELIST = [
  39.         B2bPlatformPage::class,
  40.         OffcanvasCartPage::class
  41.     ];
  42.     public const SW_LOGIN_PAGE_WHITELIST = [
  43.         'frontend.account.login',
  44.         'frontend.account.login.page',
  45.         'frontend.account.recover.page',
  46.         'frontend.b2b_create_employee_invitation.page'
  47.     ];
  48.     public const B2B_LOGIN_PAGE_WHITELIST = [
  49.         'frontend.b2b_account.login.page',
  50.         'frontend.b2b_account.login.login'
  51.     ];
  52.     public const B2B_GENERAL_PAGE_WHITELIST = [
  53.         'frontend.b2b_account_activation.page',
  54.         'frontend.b2b_employee.recover.password.page',
  55.         'frontend.b2b_employee.recover.password.reset',
  56.         'frontend.b2b_account.registration.page',
  57.         'frontend.b2b_account.registration.register',
  58.         'frontend.b2b_passwordless_login.page',
  59.         'frontend.b2b_passwordless_login.post',
  60.         'frontend.b2b_account.registration.register',
  61.         'frontend.b2b_account.registration.save',
  62.         'frontend.b2b_create_employee_invitation.page',
  63.         'frontend.b2b_passwordless_login.page'
  64.     ];
  65.     public const ROUTE_CREATE_EMPLOYEE 'frontend.b2b_create_employee';
  66.     public const ROUTE_B2B_PLATFORM 'frontend.b2b_platform.index';
  67.     public const ROUTE_B2B_PLATFORM_PATH 'frontend.b2b_platform.path';
  68.     public const ROUTE_SELECT_EMPLOYEE_ADMINISTRATOR 'frontend.b2b_select_employee_admin';
  69.     private RouterInterface $router;
  70.     private SystemConfigService $configService;
  71.     private array $config;
  72.     private EntityRepositoryInterface $employeeCustomerRepository;
  73.     private EventDispatcherInterface $eventDispatcher;
  74.     public function __construct(
  75.         EventDispatcherInterface  $eventDispatcher,
  76.         RouterInterface           $router,
  77.         SystemConfigService       $configService,
  78.         EntityRepositoryInterface $employeeCustomerRepository
  79.     )
  80.     {
  81.         $this->eventDispatcher $eventDispatcher;
  82.         $this->router $router;
  83.         $this->configService $configService;
  84.         $this->employeeCustomerRepository $employeeCustomerRepository;
  85.     }
  86.     public static function getSubscribedEvents(): array
  87.     {
  88.         return [
  89.             KernelEvents::RESPONSE => 'onKernelResponse',
  90.         ];
  91.     }
  92.     public function onKernelResponse(ResponseEvent $event)
  93.     {
  94.         if($this->isWatcherProxyRequest($event->getRequest())) {
  95.             return;
  96.         }
  97.         if($event->getRequestType() === HttpKernelInterface::SUB_REQUEST) {
  98.             return;
  99.         }
  100.         if (!$event->getResponse() instanceof StorefrontResponse) {
  101.             return;
  102.         }
  103.     
  104.         /** @var SalesChannelContext $context */
  105.         $context $event->getResponse()->getContext();
  106.         if (empty($context)) {
  107.             $this->config $this->getPluginConfig();
  108.         } else {
  109.             $this->config $this->getPluginConfig($context->getSalesChannelId());
  110.         }
  111.         if (empty($context) || empty($context->getCustomer())) {
  112.             $this->checkShopAccessibility($event);
  113.             return;
  114.         }
  115.         if (!$this->hasB2bPlatformContext($context)) {
  116.             return;
  117.         }
  118.         $responseData $event->getResponse()->getData();
  119.         if (!isset($responseData['page']) || !$responseData['page'] instanceof Page) {
  120.             return;
  121.         }
  122.         $this->eventDispatcher->dispatch(new BeforeB2bRedirectEvent($context));
  123.         $page $responseData['page'];
  124.         $b2bPlatformContext $this->getB2bPlatformContext($context);
  125.         $isSalesRepresentative = !$this->isSalesRepresentativeLoggedInAsCustomer($context) && $b2bPlatformContext->isSalesRepresentative();
  126.         /**
  127.          *  As B2b Customer select first employee administrator if not set
  128.          */
  129.         $this->eventDispatcher->dispatch(new BeforeB2bRedirectEmployeeFirstAdminEvent($context));
  130.         if (!$b2bPlatformContext->isEmployee() && !$isSalesRepresentative && $this->employeeExists($context)) {
  131.             if ($page instanceof B2bSelectEmployeeAdministratorPage || $page instanceof OffcanvasCartPage) {
  132.                 return;
  133.             }
  134.             if (!$this->employeeAdministratorExists($context)) {
  135.                 return;
  136.             }
  137.             $redirectResponse = new RedirectResponse($this->router->generate(self::ROUTE_SELECT_EMPLOYEE_ADMINISTRATOR));
  138.             $event->setResponse($redirectResponse);
  139.             return;
  140.         }
  141.         /**
  142.          * Redirect B2b Customers without employees to the employee creation
  143.          */
  144.         $this->eventDispatcher->dispatch(new BeforeB2bRedirectEmployeeCreationPageEvent($context));
  145.         if (!$b2bPlatformContext->isEmployee() && !$isSalesRepresentative) {
  146.             if ($page instanceof B2bCreateEmployeePage || $page instanceof OffcanvasCartPage) {
  147.                 return;
  148.             }
  149.             $request $event->getRequest();
  150.             $params = [];
  151.             if(($route $request->attributes->get('_route')) !== self::ROUTE_CREATE_EMPLOYEE) {
  152.                 $params = [
  153.                     'redirectTo' => $route,
  154.                     'redirectParameters' => json_encode($request->attributes->get('_route_params'))
  155.                 ];
  156.             }
  157.             $redirectResponse = new RedirectResponse($this->router->generate(self::ROUTE_CREATE_EMPLOYEE$params));
  158.             $event->setResponse($redirectResponse);
  159.             return;
  160.         }
  161.         /**
  162.          * Redirect b2b customer from the account page to the b2b platform page
  163.          */
  164.         $this->eventDispatcher->dispatch(new BeforeB2bRedirectAccountPageEvent($context));
  165.         if ($b2bPlatformContext->isEmployee() && !$b2bPlatformContext->isSalesRepresentative()) {
  166.             if ($page instanceof AccountOverviewPage) {
  167.                 $redirectResponse = new RedirectResponse($this->router->generate(self::ROUTE_B2B_PLATFORM_PATH, [
  168.                     'path' => 'account'
  169.                 ]));
  170.                 $event->setResponse($redirectResponse);
  171.                 return;
  172.             }
  173.             if ($page instanceof AccountOrderPage) {
  174.                 $redirectResponse = new RedirectResponse($this->router->generate(self::ROUTE_B2B_PLATFORM_PATH, [
  175.                     'path' => 'order'
  176.                 ]));
  177.                 $event->setResponse($redirectResponse);
  178.                 return;
  179.             }
  180.             if ($page instanceof AccountProfilePage) {
  181.                 $redirectResponse = new RedirectResponse($this->router->generate(self::ROUTE_B2B_PLATFORM_PATH, [
  182.                     'path' => 'account'
  183.                 ]));
  184.                 $event->setResponse($redirectResponse);
  185.                 return;
  186.             }
  187.             if ($page instanceof AddressListingPage) {
  188.                 $request $event->getRequest();
  189.                 if ($request->getPathInfo() !== '/account/address') {
  190.                     return;
  191.                 }
  192.                 $redirectResponse = new RedirectResponse($this->router->generate(self::ROUTE_B2B_PLATFORM_PATH, [
  193.                     'path' => 'address'
  194.                 ]));
  195.                 $event->setResponse($redirectResponse);
  196.                 return;
  197.             }
  198.             if ($page instanceof AddressDetailPage) {
  199.                 $redirectResponse = new RedirectResponse($this->router->generate(self::ROUTE_B2B_PLATFORM_PATH, [
  200.                     'path' => 'address/' $page->getAddress()->getId()
  201.                 ]));
  202.                 $event->setResponse($redirectResponse);
  203.                 return;
  204.             }
  205.         }
  206.         /**
  207.          * Redirect sales representative to his allowed routes
  208.          */
  209.         $this->eventDispatcher->dispatch(new BeforeB2bRedirectSalesRepresentativeEvent($context));
  210.         if ($isSalesRepresentative) {
  211.             if (in_array(get_class($page), self::SALES_REPRESENTATIVE_PAGE_WHITELIST)) {
  212.                 return;
  213.             }
  214.             $redirectResponse = new RedirectResponse($this->router->generate(self::ROUTE_B2B_PLATFORM));
  215.             $event->setResponse($redirectResponse);
  216.         }
  217.     }
  218.     private function checkShopAccessibility(ResponseEvent $event)
  219.     {
  220.         $closedShopControllerWhitelist = [];
  221.         if (isset($this->config) && isset($this->config['closedShopControllerWhitelist'])) {
  222.             $closedShopControllerWhitelist preg_split("/\r\n|\n|\r/"$this->config['closedShopControllerWhitelist']);
  223.         }
  224.         if (in_array($event->getRequest()->get('_route'), self::SW_LOGIN_PAGE_WHITELIST)) {
  225.             if ($event->getRequest()->get('_route') === 'frontend.account.login.page' &&
  226.                 !in_array($this->config['closedShopRedirectTarget'], self::SW_LOGIN_PAGE_WHITELIST)
  227.             ) {
  228.                 if (isset($this->config['closedShop']) && $this->config['closedShop'] === true) {
  229.                     $this->generateRouteResponse($event);
  230.                 }
  231.             }
  232.             return;
  233.         }
  234.         if (!isset($this->config['closedShop']) || $this->config['closedShop'] !== true) {
  235.             if (in_array($event->getRequest()->get('_route'), self::B2B_LOGIN_PAGE_WHITELIST)) {
  236.                 $redirectResponse = new RedirectResponse($this->router->generate('frontend.account.login'));
  237.                 $event->setResponse($redirectResponse);
  238.             }
  239.             return;
  240.         }
  241.         if (in_array($event->getRequest()->get('_route'), self::B2B_LOGIN_PAGE_WHITELIST)) {
  242.             return;
  243.         }
  244.         if (in_array($event->getRequest()->get('_route'), self::B2B_GENERAL_PAGE_WHITELIST)) {
  245.             return;
  246.         }
  247.         if (in_array($event->getRequest()->get('_route'), $closedShopControllerWhitelist)) {
  248.             return;
  249.         }
  250.         if (isset($this->config['closedShopWhitelist'])) {
  251.             $responseData $event->getResponse()->getData();
  252.             /** @var Page|null $page */
  253.             $page $responseData['page'] ?? null;
  254.             /** @var CmsPageEntity $cmsPage */
  255.             $cmsPage $responseData['cmsPage'] ?? null;
  256.             if (empty($page) && empty($cmsPage)) {
  257.                 return;
  258.             }
  259.             // check if page is in whitelist
  260.             if (isset($page) && method_exists($page,
  261.                     'getCmsPage') && $page->getCmsPage() !== null && in_array($page->getCmsPage()->getId(),
  262.                     $this->config['closedShopWhitelist'])) {
  263.                 return;
  264.             }
  265.             // check if cms page is in whitelist
  266.             if (isset($cmsPage) && in_array($cmsPage->getId(), $this->config['closedShopWhitelist'])) {
  267.                 return;
  268.             }
  269.         }
  270.         if (isset($responseData['cookieGroups'])) {
  271.             return;
  272.         }
  273.         $this->generateRouteResponse($event);
  274.     }
  275.     private function generateRouteResponse(ResponseEvent $event)
  276.     {
  277.         $redirectResponse = new RedirectResponse($this->router->generate('frontend.account.login'));
  278.         if (isset($this->config['closedShopRedirectTarget'])) {
  279.             $redirectResponse = new RedirectResponse($this->router->generate($this->config['closedShopRedirectTarget']));
  280.         }
  281.         $event->setResponse($redirectResponse);
  282.     }
  283.     private function getPluginConfig(?string $salesChannelId null): array
  284.     {
  285.         return $this->configService->get('B2bSellersCore.config'$salesChannelId);
  286.     }
  287.     private function employeeAdministratorExists(SalesChannelContext $context): bool
  288.     {
  289.         $criteria = new Criteria();
  290.         $criteria->addFilter(new EqualsFilter('customerId'$context->getCustomer()->getId()));
  291.         /** @var EmployeeCustomerCollection $employeeCustomers */
  292.         $employeeCustomers $this->employeeCustomerRepository->search($criteria,
  293.             $context->getContext())->getEntities();
  294.         return $employeeCustomers->getAdmins()->count() <= 0;
  295.     }
  296.     private function employeeExists(SalesChannelContext $context): bool
  297.     {
  298.         $criteria = new Criteria();
  299.         $criteria->addFilter(new EqualsFilter('customerId'$context->getCustomer()->getId()));
  300.         /** @var EmployeeCustomerCollection $employeeCustomers */
  301.         $employeeCustomers $this->employeeCustomerRepository->search($criteria,
  302.             $context->getContext())->getEntities();
  303.         return $employeeCustomers->count() > 0;
  304.     }
  305.     private function isWatcherProxyRequest(Request $request): bool
  306.     {
  307.         if($request->headers->get('x-b2b-platform-watcher') !== '1') {
  308.             return false;
  309.         }
  310.         $referer $request->headers->get('referer');
  311.         if(empty($referer)) {
  312.             return false;
  313.         }
  314.         if(!filter_var($refererFILTER_VALIDATE_URL)) {
  315.             return false;
  316.         }
  317.         $port parse_url($referer)['port'] ?? null;
  318.         return $port === 9998;
  319.     }
  320. }