<?php declare(strict_types=1);
namespace Acris\ShopSwitch\Components\ShopSwitch;
use Acris\ShopSwitch\Components\Exception\IpNotFoundException;
use Acris\ShopSwitch\Components\Exclusion\ExclusionService;
use Acris\ShopSwitch\Components\GeoTargeting\BotService;
use Acris\ShopSwitch\Components\ContextLanguageService;
use Acris\ShopSwitch\Components\GeoTargeting\Ip2CountryService;
use Acris\ShopSwitch\Components\GeoTargeting\IpService;
use Acris\ShopSwitch\Components\ShopSwitchHelperService;
use Acris\ShopSwitch\Components\ShopSwitchRedirectUrlService;
use Acris\ShopSwitch\Components\ShopSwitchRuleGateway;
use Acris\ShopSwitch\Components\ShopSwitchRuleService;
use Acris\ShopSwitch\Components\Struct\PossibleCountryCollection;
use Acris\ShopSwitch\Components\Struct\PossibleLanguageCollection;
use Acris\ShopSwitch\Custom\ShopSwitchRuleEntity;
use Acris\ShopSwitch\Components\Exception\ActiveDomainNotFoundException;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\Plugin\Exception\DecorationPatternException;
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
use Shopware\Core\Framework\Routing\Annotation\Since;
use Shopware\Core\System\Country\CountryEntity;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Acris\ShopSwitch\Components\PathInfoChecker;
/**
* @RouteScope(scopes={"store-api"})
*/
class ShopSwitchRoute extends AbstractShopSwitchRoute
{
public const INTEGRATION_TYPE_DIRECT = 'direct';
public const INTEGRATION_TYPE_AJAX = 'ajax';
/**
* @var ShopSwitchRuleGateway
*/
private $shopSwitchRuleGateway;
/**
* @var ShopSwitchRuleService
*/
private $shopSwitchRuleService;
/**
* @var IpService
*/
private $ipService;
/**
* @var ShopSwitchRedirectUrlService
*/
private $redirectUrlService;
/**
* @var BotService
*/
private BotService $botService;
/**
* @var ContextLanguageService
*/
private ContextLanguageService $contextLanguageService;
/**
* @var SystemConfigService
*/
private SystemConfigService $configService;
private ExclusionService $exclusionService;
private ShopSwitchHelperService $helperService;
private Ip2CountryService $ip2CountryService;
private PathInfoChecker $infoChecker;
private EntityRepositoryInterface $countryRepository;
public function __construct(
ShopSwitchRuleGateway $shopSwitchRuleGateway,
ShopSwitchRuleService $shopSwitchRuleService,
IpService $ipService,
ShopSwitchRedirectUrlService $redirectUrlService,
BotService $botService,
ContextLanguageService $contextLanguageService,
SystemConfigService $configService,
ExclusionService $exclusionService,
ShopSwitchHelperService $helperService,
Ip2CountryService $ip2CountryService,
PathInfoChecker $infoChecker,
EntityRepositoryInterface $countryRepository
) {
$this->shopSwitchRuleGateway = $shopSwitchRuleGateway;
$this->shopSwitchRuleService = $shopSwitchRuleService;
$this->ipService = $ipService;
$this->redirectUrlService = $redirectUrlService;
$this->botService = $botService;
$this->contextLanguageService = $contextLanguageService;
$this->configService = $configService;
$this->exclusionService = $exclusionService;
$this->helperService = $helperService;
$this->ip2CountryService = $ip2CountryService;
$this->infoChecker = $infoChecker;
$this->countryRepository = $countryRepository;
}
public function getDecorated(): AbstractShopSwitchRoute
{
throw new DecorationPatternException(self::class);
}
/**
* @Since("6.3.4.0")
* @Route("/store-api/acris-shop-switch-rules/{activeDomainId}", name="store-api.acris-shop-switch.rules", methods={"GET", "POST"})
*/
public function load(string $activeDomainId, Request $request, Context $context, ?string $languageId, ?string $fixedLanguageId = null, ?string $fixedCountryIso = null): ShopSwitchRouteResponse
{
$activeDomainEntity = $this->helperService->getDomainById($activeDomainId, $context);
if(empty($languageId)) {
$languageId = $activeDomainEntity->getLanguageId();
}
$activeSalesChannel = $activeDomainEntity->getSalesChannel();
$salesChannelId = $activeSalesChannel->getId();
if(empty($fixedLanguageId) === true) {
$browserLanguageIso = $this->getBrowserLanguageIso($request);
$context = $this->contextLanguageService->getContextByLanguageId($context, $languageId);
$languageIds = $this->getLanguageIdsByLanguageIso($browserLanguageIso, $context, $salesChannelId);
$languageId = reset($languageIds);
if($this->configService->get('AcrisShopSwitchCS.config.modalWindowLanguage', $salesChannelId) === 'browserLanguage' && !empty($languageId)) {
$context = $this->switchContextByBrowserLanguage($languageId, $context);
}
} else {
$languageIds = [$fixedLanguageId];
}
$activeRule = $this->shopSwitchRuleGateway->getActiveRuleByDomain($activeDomainId, $context);
if($activeRule instanceof ShopSwitchRuleEntity) {
$domainCollections = $activeRule->getDomains();
if($domainCollections) {
$this->infoChecker->collectPathInfoExistencesForDomains($request,$domainCollections);
}
}
$activeDomainUrl = $this->redirectUrlService->getUrlForActiveDomain($request, $activeDomainEntity);
if(!$activeRule instanceof ShopSwitchRuleEntity) {
return new ShopSwitchRouteResponse(new ShopSwitchResult($activeDomainId, new PossibleCountryCollection(), new PossibleLanguageCollection(), null, $activeDomainUrl, true));
}
$activeRuleDomain = $this->shopSwitchRuleService->getActiveRuleDomain($activeDomainId, $activeRule);
if(empty($activeRuleDomain)) {
throw new ActiveDomainNotFoundException();
}
if($this->botService->isBot($request) === true) {
return new ShopSwitchRouteResponse(new ShopSwitchResult($activeDomainId, new PossibleCountryCollection(), new PossibleLanguageCollection(), null, $activeDomainUrl, true));
}
if($this->exclusionService->isExcluded($this->ipService->getClientIp($request, $salesChannelId), $request, $context) === true) {
return new ShopSwitchRouteResponse(new ShopSwitchResult($activeDomainId, new PossibleCountryCollection(), new PossibleLanguageCollection(), null, $activeDomainUrl, true));
}
// always try to get the country from the ip lookup (also when just language selection is used to set the shipping country - could be change via new config value if wanted)
if(empty($fixedCountryIso) === true) {
$ip = $this->ipService->getClientIp($request, $salesChannelId);
if(empty($ip)) {
throw new IpNotFoundException();
}
$fixedCountryIso = $this->ip2CountryService->ip2country($ip, $salesChannelId);
if(empty($fixedCountryIso) === true) {
$defaultCountryId = $this->configService->get('AcrisShopSwitchCS.config.fallbackCountry', $salesChannelId);
if(!empty($defaultCountryId)) {
$defaultCountry = $this->countryRepository->search(new Criteria([$defaultCountryId]), $context)->first();
if($defaultCountry instanceof CountryEntity) {
$fixedCountryIso = $defaultCountry->getIso();
}
}
}
}
return $this->helperService->getValidResponse($activeDomainId, $activeRule, $activeDomainUrl, $request, $activeSalesChannel, $context, $activeRuleDomain, $languageIds, $fixedCountryIso);
}
private function getBrowserLanguageIso(Request $request): ?string
{
$browserAcceptLanguage = $request->server->get('HTTP_ACCEPT_LANGUAGE');
if(empty($browserAcceptLanguage) === true) {
return $browserAcceptLanguage;
}
if(strpos($browserAcceptLanguage,",") == 2){
return strtolower(substr($browserAcceptLanguage, 0, 2));
}else {
return strtolower(substr($browserAcceptLanguage, 0, 5));
}
}
private function getLanguageIdsByLanguageIso(?string $languageIso, Context $context, string $salesChannelId): array
{
if(empty($languageIso) === true) {
return [];
}
$languageIds = $this->contextLanguageService->getLanguageIdByIso($languageIso, $context);
if(empty($languageIds) === true) {
$languageIdFromConfig = $this->configService->get('AcrisShopSwitchCS.config.fallbackModalWindowLanguage', $salesChannelId);
if(!empty($languageIdFromConfig)) {
$languageIds = [$languageIdFromConfig];
}
}
return $languageIds;
}
private function switchContextByBrowserLanguage(?string $languageId, Context $context): Context
{
if(empty($languageId) === true) {
return $context;
}
return $this->contextLanguageService->getContextByLanguageId($context, $languageId);
}
}