diff --git a/README.md b/README.md index 42a862c..1f950ab 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ Does actually the same as the update command and preferred in CI-Workflow: - [Localized Error Documents](docs/40_LocaleErrorDocument.md): Learn how to create localized error documents. - [Custom Locale Adapter](docs/50_CustomLocaleAdapter.md): Learn how to create a custom locale adapter. - [Redirector Adapter](docs/51_RedirectorAdapter.md): Learn more about redirector adapter and how to implement a custom one. +- [Pimcore Redirects with I18n](docs/52_PimcoreRedirects.md): Learn how to create localized pimcore redirects. - [Code Examples](docs/60_CodeExamples.md): See some examples. - [Context Switch Event](docs/70_ContextSwitch.md): Detect zone/language/country switches. - [Canonical Links](docs/80_CanonicalLinks.md): Canonical links in hardlinks. diff --git a/UPGRADE.md b/UPGRADE.md index aba61b3..7248625 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -6,6 +6,9 @@ After every update you should check the pimcore extension manager. Just click the "update" button or execute the migration command to finish the bundle update. +#### Update from Version 3.0.0 to Version 3.1.0 +- **[NEW FEATURE]**: Allow [pimcore redirect modification](https://github.com/dachcom-digital/pimcore-i18n/issues/33). + #### Update from Version 2.x to Version 3.0.0 - **[NEW FEATURE]**: Pimcore 6.0.0 ready diff --git a/docs/52_PimcoreRedirects.md b/docs/52_PimcoreRedirects.md new file mode 100644 index 0000000..743a315 --- /dev/null +++ b/docs/52_PimcoreRedirects.md @@ -0,0 +1,32 @@ +# Pimcore Redirects with I18n + +![Pimcore Redirects](https://user-images.githubusercontent.com/700119/63445786-4917fe80-c439-11e9-8007-e19576cdf8bc.png) + +If you want to implement the i18n redirect guesser, you need to add `{i18n_localized_target_page=4}` instead of the document path in the `target` section. + +## Facts +- Be sure that your documents are connected, otherwise the detection won`t work. +- If you want to redirect an unknown host (Example A), be sure you set the redirect `priority` to `99`. + +## Example + +### Given Structure +**Domain**: `mydomain.com` +**Document Tree**: +- de + - kollektion + - kampagne +- en + - collection + - landingpage + +## Example A: +- Enter host `my-old-collection-domain.com` +- i18n should redirect to `mydomain.com/de/kollektion` if user is german +- i18n should redirect to `mydomain.com/en/collection` if user is english + +## Example B: +- Enter host `mydomain.com/landingpage` +- i18n should redirect to `mydomain.com/de/kampagne` if user is german +- i18n should redirect to `mydomain.com/en/landingpage` if user is english + diff --git a/src/I18nBundle/EventListener/ContextSwitchDetectorListener.php b/src/I18nBundle/EventListener/ContextSwitchDetectorListener.php new file mode 100644 index 0000000..959bde0 --- /dev/null +++ b/src/I18nBundle/EventListener/ContextSwitchDetectorListener.php @@ -0,0 +1,316 @@ +eventDispatcher = $eventDispatcher; + $this->documentResolver = $documentResolver; + $this->zoneManager = $zoneManager; + $this->documentHelper = $documentHelper; + $this->requestValidatorHelper = $requestValidatorHelper; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return [ + KernelEvents::REQUEST => [ + ['onKernelRequest', 1] // after i18n detector listener + ] + ]; + } + + /** + * Apply this method after the pimcore context resolver. + * + * @param GetResponseEvent $event + * + * @throws \Exception + */ + public function onKernelRequest(GetResponseEvent $event) + { + if ($event->isMasterRequest() === false) { + return; + } + + $this->request = $event->getRequest(); + $this->document = $this->documentResolver->getDocument($this->request); + + if ($this->isValidRequest() === false) { + return; + } + + $this->setDocumentLocale(); + + // check if zone, language or country has been changed, + // trigger event for 3th party. + $this->detectContextSwitch(); + + // update session + $this->updateSessionData(); + } + + /** + * Important: ContextSwitch only works in same domain levels. + * Since there is no way for simple cross-domain session ids, + * the zone switch has no relevance. + * + * @throws \Exception + */ + private function detectContextSwitch() + { + $session = $this->getSessionData(); + $currentZoneId = $this->zoneManager->getCurrentZoneInfo('zone_id'); + + $localeHasSwitched = false; + $languageHasSwitched = false; + $countryHasSwitched = false; + $zoneHasSwitched = false; + + if (is_null($session['lastLocale']) || (!is_null($session['lastLocale']) && $this->documentLocale !== $session['lastLocale'])) { + $localeHasSwitched = true; + } + + if (is_null($session['lastLanguage']) || (!is_null($session['lastLanguage']) && $this->documentLanguage !== $session['lastLanguage'])) { + $languageHasSwitched = true; + } + + if ($session['lastCountry'] !== false && (!is_null($session['lastCountry']) && $this->documentCountry !== $session['lastCountry'])) { + $countryHasSwitched = true; + $localeHasSwitched = true; + } + + if ($currentZoneId !== $session['lastZoneId']) { + $zoneHasSwitched = true; + } + + if ($zoneHasSwitched || $localeHasSwitched || $languageHasSwitched || $countryHasSwitched) { + $params = [ + 'zoneHasSwitched' => $zoneHasSwitched, + 'zoneFrom' => $session['lastZoneId'], + 'zoneTo' => $currentZoneId, + 'localeHasSwitched' => $localeHasSwitched, + 'localeFrom' => $session['lastLocale'], + 'localeTo' => $this->documentLocale, + 'languageHasSwitched' => $languageHasSwitched, + 'languageFrom' => $session['lastLanguage'], + 'languageTo' => $this->documentLanguage, + 'countryHasSwitched' => $countryHasSwitched, + 'countryFrom' => $session['lastCountry'], + 'countryTo' => $this->documentCountry + ]; + + if ($zoneHasSwitched === true) { + Logger::log( + sprintf( + 'switch zone: from %s to %s. triggered by: %s', + $session['lastZoneId'], + $currentZoneId, + $this->request->getRequestUri() + ) + ); + } + + if ($localeHasSwitched === true) { + Logger::log( + sprintf( + 'switch locale: from %s to %s. triggered by: %s', + $session['lastLocale'], + $this->documentLocale, + $this->request->getRequestUri() + ) + ); + } + + if ($languageHasSwitched === true) { + Logger::log( + sprintf( + 'switch language: from %s to %s. triggered by: %s', + $session['lastLanguage'], + $this->documentLanguage, + $this->request->getRequestUri() + ) + ); + } + + if ($countryHasSwitched === true) { + Logger::log( + sprintf( + 'switch country: from %s to %s. triggered by: %s', + $session['lastCountry'], + $this->documentCountry, + $this->request->getRequestUri() + ) + ); + } + + $this->eventDispatcher->dispatch(I18nEvents::CONTEXT_SWITCH, new ContextSwitchEvent($params)); + } + } + + /** + * @return array + */ + protected function getSessionData() + { + /** @var NamespacedAttributeBag $bag */ + $bag = $this->request->getSession()->getBag('i18n_session'); + + $data = [ + 'lastLocale' => null, + 'lastLanguage' => null, + 'lastCountry' => null, + 'lastZoneId' => null + ]; + + if ($bag->has('lastLocale')) { + $data['lastLocale'] = $bag->get('lastLocale'); + } + + if ($bag->has('lastLanguage')) { + $data['lastLanguage'] = $bag->get('lastLanguage'); + } + + if ($bag->get('lastCountry')) { + $data['lastCountry'] = $bag->get('lastCountry'); + } + + //if no zone as been defined, zone id is always NULL. + $data['lastZoneId'] = $bag->get('lastZoneId'); + + return $data; + } + + /** + * @throws \Exception + */ + protected function updateSessionData() + { + $currentZoneId = $this->zoneManager->getCurrentZoneInfo('zone_id'); + + /** @var NamespacedAttributeBag $bag */ + $bag = $this->request->getSession()->getBag('i18n_session'); + + if (!empty($this->documentLocale)) { + $bag->set('lastLocale', $this->documentLocale); + } + + if (!empty($this->documentLanguage)) { + $bag->set('lastLanguage', $this->documentLanguage); + } + + if (!empty($this->documentCountry)) { + $bag->set('lastCountry', $this->documentCountry); + } + + $bag->set('lastZoneId', $currentZoneId); + } + + /** + * @return bool + */ + protected function isValidRequest() + { + if (empty($this->document)) { + return false; + } + + return $this->requestValidatorHelper->isValidForRedirect($this->request, false); + } + + /** + * @throws \Exception + */ + protected function setDocumentLocale() + { + $i18nType = $this->zoneManager->getCurrentZoneInfo('mode'); + + $documentLocaleData = $this->documentHelper->getDocumentLocaleData($this->document, $i18nType); + + $this->documentLocale = $documentLocaleData['documentLocale']; + $this->documentLanguage = $documentLocaleData['documentLanguage']; + $this->documentCountry = $documentLocaleData['documentCountry']; + } +} diff --git a/src/I18nBundle/EventListener/DetectorListener.php b/src/I18nBundle/EventListener/DetectorListener.php index ceb4160..2689f92 100644 --- a/src/I18nBundle/EventListener/DetectorListener.php +++ b/src/I18nBundle/EventListener/DetectorListener.php @@ -3,20 +3,22 @@ namespace I18nBundle\EventListener; use I18nBundle\Helper\CookieHelper; +use I18nBundle\Helper\DocumentHelper; +use I18nBundle\Helper\RequestValidatorHelper; use Pimcore\Cache; use Pimcore\Http\Request\Resolver\EditmodeResolver; -use Pimcore\Logger; use I18nBundle\Adapter\Redirector\RedirectorBag; use I18nBundle\Adapter\Redirector\RedirectorInterface; use I18nBundle\Configuration\Configuration; use I18nBundle\Definitions; -use I18nBundle\Event\ContextSwitchEvent; -use I18nBundle\I18nEvents; use I18nBundle\Manager\ContextManager; use I18nBundle\Manager\PathGeneratorManager; use I18nBundle\Manager\ZoneManager; use I18nBundle\Registry\RedirectorRegistry; use I18nBundle\Tool\System; +use Pimcore\Model\Site; +use Pimcore\Tool\Admin; +use Pimcore\Tool\Authentication; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\Event\GetResponseEvent; @@ -27,19 +29,10 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Pimcore\Model\Document; use Pimcore\Http\Request\Resolver\DocumentResolver; -use Pimcore\Http\Request\Resolver\PimcoreContextResolver; -use Pimcore\Bundle\CoreBundle\EventListener\Traits\PimcoreContextAwareTrait; use Symfony\Component\Templating\EngineInterface; class DetectorListener implements EventSubscriberInterface { - use PimcoreContextAwareTrait; - - /** - * @var string - */ - private $i18nType = 'language'; - /** * @var string */ @@ -51,7 +44,7 @@ class DetectorListener implements EventSubscriberInterface private $validLocales = []; /** - * @var \Pimcore\Model\Document + * @var Document */ private $document = null; @@ -70,6 +63,11 @@ class DetectorListener implements EventSubscriberInterface */ private $documentCountry = null; + /** + * @var Request + */ + private $request; + /** * @var EngineInterface */ @@ -90,6 +88,11 @@ class DetectorListener implements EventSubscriberInterface */ protected $redirectorRegistry; + /** + * @var DocumentResolver + */ + protected $documentResolver; + /** * @var ZoneManager */ @@ -106,32 +109,32 @@ class DetectorListener implements EventSubscriberInterface protected $pathGeneratorManager; /** - * @var DocumentResolver + * @var EditmodeResolver */ - protected $documentResolver; + protected $editmodeResolver; /** - * @var EditmodeResolver + * @var DocumentHelper */ - protected $editmodeResolver; + protected $documentHelper; /** - * @var Request + * @var RequestValidatorHelper */ - protected $request; + protected $requestValidatorHelper; /** - * DetectorListener constructor. - * - * @param EngineInterface $templating - * @param Configuration $configuration - * @param CookieHelper $cookieHelper - * @param RedirectorRegistry $redirectorRegistry - * @param DocumentResolver $documentResolver - * @param ZoneManager $zoneManager - * @param ContextManager $contextManager - * @param PathGeneratorManager $pathGeneratorManager - * @param EditmodeResolver $editmodeResolver + * @param EngineInterface $templating + * @param Configuration $configuration + * @param CookieHelper $cookieHelper + * @param RedirectorRegistry $redirectorRegistry + * @param DocumentResolver $documentResolver + * @param ZoneManager $zoneManager + * @param ContextManager $contextManager + * @param PathGeneratorManager $pathGeneratorManager + * @param EditmodeResolver $editmodeResolver + * @param DocumentHelper $documentHelper + * @param RequestValidatorHelper $requestValidatorHelper */ public function __construct( EngineInterface $templating, @@ -142,7 +145,9 @@ public function __construct( ZoneManager $zoneManager, ContextManager $contextManager, PathGeneratorManager $pathGeneratorManager, - EditmodeResolver $editmodeResolver + EditmodeResolver $editmodeResolver, + DocumentHelper $documentHelper, + RequestValidatorHelper $requestValidatorHelper ) { $this->templating = $templating; $this->configuration = $configuration; @@ -153,6 +158,8 @@ public function __construct( $this->contextManager = $contextManager; $this->pathGeneratorManager = $pathGeneratorManager; $this->editmodeResolver = $editmodeResolver; + $this->documentHelper = $documentHelper; + $this->requestValidatorHelper = $requestValidatorHelper; } /** @@ -161,39 +168,17 @@ public function __construct( public static function getSubscribedEvents() { return [ - KernelEvents::EXCEPTION => ['onKernelException', 20], //before responseExceptionListener + KernelEvents::EXCEPTION => [ + ['onKernelException', 20] // before responseExceptionListener + ], KernelEvents::REQUEST => [ - ['onKernelRequestLocale', 17], // before symfony LocaleListener - ['onKernelRequest', 2] // after pimcore context resolver + ['onKernelRequestLocale', 17], // before symfony LocaleListener + ['onKernelRequest', 2] // after pimcore context resolver ], KernelEvents::RESPONSE => 'onKernelResponse' ]; } - /** - * @param Request $request - * - * @throws \Exception - */ - private function initI18nSystem($request) - { - //initialize all managers! - $this->zoneManager->initZones(); - - $document = null; - if ($this->document instanceof Document) { - $document = $this->document; - if ($this->document instanceof Document\Hardlink\Wrapper\WrapperInterface) { - /** @var Document\Hardlink\Wrapper $wrapperDocument */ - $wrapperDocument = $this->document; - $document = $wrapperDocument->getHardLinkSource(); - } - } - - $this->contextManager->initContext($this->zoneManager->getCurrentZoneInfo('mode'), $document); - $this->pathGeneratorManager->initPathGenerator($request->attributes->get('pimcore_request_source')); - } - /** * @param GetResponseForExceptionEvent $event * @@ -206,7 +191,6 @@ public function onKernelException(GetResponseForExceptionEvent $event) } $this->initI18nSystem($event->getRequest()); - $this->document = $this->documentResolver->getDocument($this->request); $locale = $event->getRequest()->getLocale(); @@ -217,17 +201,17 @@ public function onKernelException(GetResponseForExceptionEvent $event) } //fallback. - Cache\Runtime::set('i18n.locale', $event->getRequest()->getLocale()); + Cache\Runtime::set('i18n.locale', $locale); Cache\Runtime::set('i18n.languageIso', strtolower($languageIso)); Cache\Runtime::set('i18n.countryIso', Definitions::INTERNATIONAL_COUNTRY_NAMESPACE); } /** - * If we're in static route context, we need to check the request locale since it could be a invalid one from the url (like - * en-us). Always use the document locale then! + * If we're in static route context, we need to check the request locale since it could be a invalid one from the url + * (like en-us). Always use the document locale then! * - * Since symfony tries to locate the current locale in LocaleListener via the request attribute "_locale", we need to trigger - * this event earlier! + * Since symfony tries to locate the current locale in LocaleListener via the request attribute "_locale", + * we need to trigger his event earlier! * * @param GetResponseEvent $event * @@ -235,11 +219,18 @@ public function onKernelException(GetResponseForExceptionEvent $event) */ public function onKernelRequestLocale(GetResponseEvent $event) { - if ($this->setValidRequest($event) === false) { + if ($event->isMasterRequest() === false) { return; } - $this->setDocumentLocale(); + $this->request = $event->getRequest(); + $this->document = $this->documentResolver->getDocument($this->request); + + if ($this->isValidRequest() === false) { + return; + } + + $this->setDocumentLocale('language'); $requestSource = $this->request->attributes->get('pimcore_request_source'); if ($requestSource === 'staticroute' && !empty($this->documentLocale) && $this->request->attributes->get('_locale') !== $this->documentLocale) { @@ -256,7 +247,14 @@ public function onKernelRequestLocale(GetResponseEvent $event) */ public function onKernelRequest(GetResponseEvent $event) { - if ($this->setValidRequest($event) === false) { + if ($event->isMasterRequest() === false) { + return; + } + + $this->request = $event->getRequest(); + $this->document = $this->documentResolver->getDocument($this->request); + + if ($this->isValidRequest() === false) { return; } @@ -265,13 +263,13 @@ public function onKernelRequest(GetResponseEvent $event) $currentRouteName = $this->request->get('_route'); $requestSource = $this->request->attributes->get('pimcore_request_source'); - $this->i18nType = $this->zoneManager->getCurrentZoneInfo('mode'); - - $this->setDocumentLocale(); - $this->validLocales = $this->zoneManager->getCurrentZoneLocaleAdapter()->getActiveLocales(); $this->defaultLocale = $this->zoneManager->getCurrentZoneLocaleAdapter()->getDefaultLocale(); + $i18nType = $this->zoneManager->getCurrentZoneInfo('mode'); + + $this->setDocumentLocale($i18nType); + /** * If a root node hardlink is requested e.g. /en-us, pimcore gets the locale from the source, which is "quite" wrong. */ @@ -302,7 +300,7 @@ public function onKernelRequest(GetResponseEvent $event) if ($this->canRedirect() && $validForRedirect === true) { $options = [ - 'i18nType' => $this->i18nType, + 'i18nType' => $i18nType, 'request' => $this->request, 'document' => $this->document, 'documentLocale' => $this->documentLocale, @@ -342,16 +340,12 @@ public function onKernelRequest(GetResponseEvent $event) if (!empty($this->documentCountry)) { Cache\Runtime::set('i18n.countryIso', $this->documentCountry); } - - //check if zone, language or country has been changed, trigger event for 3th party. - $this->detectContextSwitch(); - - //update session - $this->updateSessionData(); } /** * @param FilterResponseEvent $event + * + * @throws \Exception */ public function onKernelResponse(FilterResponseEvent $event) { @@ -359,7 +353,7 @@ public function onKernelResponse(FilterResponseEvent $event) return; } - if (!$this->matchesPimcoreContext($event->getRequest(), PimcoreContextResolver::CONTEXT_DEFAULT)) { + if ($this->requestValidatorHelper->matchesDefaultPimcoreContext($event->getRequest()) === false) { return; } @@ -373,7 +367,8 @@ public function onKernelResponse(FilterResponseEvent $event) $registryConfig = $this->configuration->getConfig('registry'); $available = isset($registryConfig['redirector']['cookie']) - ? $registryConfig['redirector']['cookie']['enabled'] : true; + ? $registryConfig['redirector']['cookie']['enabled'] + : true; //check if we're allowed to bake a cookie at the first place! if ($available === false) { @@ -417,173 +412,36 @@ public function onKernelResponse(FilterResponseEvent $event) } /** - * Important: ContextSwitch only works in same domain levels. - * Since there is no way for simple cross-domain session ids, - * the zone switch will be sort of useless most of the time. :(. + * @param Request $request + * + * @throws \Exception */ - private function detectContextSwitch() + private function initI18nSystem($request) { - if (!$this->isValidI18nCheckRequest($this->request)) { - return; - } - - $session = $this->getSessionData(); - $currentZoneId = $this->zoneManager->getCurrentZoneInfo('zone_id'); - - $localeHasSwitched = false; - $languageHasSwitched = false; - $countryHasSwitched = false; - $zoneHasSwitched = false; - - if (is_null($session['lastLocale']) || (!is_null($session['lastLocale']) && $this->documentLocale !== $session['lastLocale'])) { - $localeHasSwitched = true; - } - - if (is_null($session['lastLanguage']) || (!is_null($session['lastLanguage']) && $this->documentLanguage !== $session['lastLanguage'])) { - $languageHasSwitched = true; - } - - if ($session['lastCountry'] !== false && (!is_null($session['lastCountry']) && $this->documentCountry !== $session['lastCountry'])) { - $countryHasSwitched = true; - $localeHasSwitched = true; - } - - if ($currentZoneId !== $session['lastZoneId']) { - $zoneHasSwitched = true; - } - - if ($zoneHasSwitched || $localeHasSwitched || $languageHasSwitched || $countryHasSwitched) { - $params = [ - 'zoneHasSwitched' => $zoneHasSwitched, - 'zoneFrom' => $session['lastZoneId'], - 'zoneTo' => $currentZoneId, - 'localeHasSwitched' => $localeHasSwitched, - 'localeFrom' => $session['lastLocale'], - 'localeTo' => $this->documentLocale, - 'languageHasSwitched' => $languageHasSwitched, - 'languageFrom' => $session['lastLanguage'], - 'languageTo' => $this->documentLanguage, - 'countryHasSwitched' => $countryHasSwitched, - 'countryFrom' => $session['lastCountry'], - 'countryTo' => $this->documentCountry - ]; - - if ($zoneHasSwitched === true) { - Logger::log( - sprintf( - 'switch zone: from %s to %s. triggered by: %s', - $session['lastZoneId'], - $currentZoneId, - $this->request->getRequestUri() - ) - ); - } - - if ($localeHasSwitched === true) { - Logger::log( - sprintf( - 'switch locale: from %s to %s. triggered by: %s', - $session['lastLocale'], - $this->documentLocale, - $this->request->getRequestUri() - ) - ); - } - - if ($languageHasSwitched === true) { - Logger::log( - sprintf( - 'switch language: from %s to %s. triggered by: %s', - $session['lastLanguage'], - $this->documentLanguage, - $this->request->getRequestUri() - ) - ); - } + $this->zoneManager->initZones(); - if ($countryHasSwitched === true) { - Logger::log( - sprintf( - 'switch country: from %s to %s. triggered by: %s', - $session['lastCountry'], - $this->documentCountry, - $this->request->getRequestUri() - ) - ); + $document = null; + if ($this->document instanceof Document) { + $document = $this->document; + if ($this->document instanceof Document\Hardlink\Wrapper\WrapperInterface) { + /** @var Document\Hardlink\Wrapper $wrapperDocument */ + $wrapperDocument = $this->document; + $document = $wrapperDocument->getHardLinkSource(); } - - \Pimcore::getEventDispatcher()->dispatch( - I18nEvents::CONTEXT_SWITCH, - new ContextSwitchEvent($params) - ); - } - } - - /** - * @return array - */ - private function getSessionData() - { - /** @var \Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag $bag */ - $bag = $this->request->getSession()->getBag('i18n_session'); - - $data = [ - 'lastLocale' => null, - 'lastLanguage' => null, - 'lastCountry' => null, - 'lastZoneId' => null - ]; - - if ($bag->has('lastLocale')) { - $data['lastLocale'] = $bag->get('lastLocale'); - } - - if ($bag->has('lastLanguage')) { - $data['lastLanguage'] = $bag->get('lastLanguage'); } - if ($bag->get('lastCountry')) { - $data['lastCountry'] = $bag->get('lastCountry'); - } - - //if no zone as been defined, zone id is always NULL. - $data['lastZoneId'] = $bag->get('lastZoneId'); - - return $data; - } - - private function updateSessionData() - { - if (!$this->isValidI18nCheckRequest($this->request)) { - return; - } - - $currentZoneId = $this->zoneManager->getCurrentZoneInfo('zone_id'); - - /** @var \Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag $bag */ - $bag = $this->request->getSession()->getBag('i18n_session'); - - if (!empty($this->documentLocale)) { - $bag->set('lastLocale', $this->documentLocale); - } - - if (!empty($this->documentLanguage)) { - $bag->set('lastLanguage', $this->documentLanguage); - } - - if (!empty($this->documentCountry)) { - $bag->set('lastCountry', $this->documentCountry); - } - - $bag->set('lastZoneId', $currentZoneId); + $this->contextManager->initContext($this->zoneManager->getCurrentZoneInfo('mode'), $document); + $this->pathGeneratorManager->initPathGenerator($request->attributes->get('pimcore_request_source')); } /** * @param string $path * * @return string + * + * @throws \Exception */ - private function getRedirectUrl($path) + protected function getRedirectUrl($path) { $config = \Pimcore\Config::getSystemConfig(); @@ -596,64 +454,24 @@ private function getRedirectUrl($path) return $endPath; } - /** - * @param Request $request - * @param bool $allowAjax - * - * @return bool - */ - private function isValidI18nCheckRequest(Request $request, $allowAjax = false) - { - if (\Pimcore\Tool::isFrontendRequestByAdmin($request)) { - return false; - } - - if (System::isInCliMode() || ($allowAjax === false && $request->isXmlHttpRequest())) { - return false; - } - - return true; - } - /** * @return bool */ - private function canRedirect() + protected function canRedirect() { return !System::isInBackend($this->request); } /** - * @param GetResponseEvent $event - * * @return bool */ - private function setValidRequest(GetResponseEvent $event) + protected function isValidRequest() { - // already initialized. - if ($this->document instanceof Document) { - return true; - } - - if ($event->isMasterRequest() === false) { - return false; - } - - $this->request = $event->getRequest(); - if (!$this->matchesPimcoreContext($this->request, PimcoreContextResolver::CONTEXT_DEFAULT)) { - return false; - } - - $this->document = $this->documentResolver->getDocument($this->request); - if (!$this->document) { + if (empty($this->document)) { return false; } - if (!$this->isValidI18nCheckRequest($this->request, true)) { - return false; - } - - return true; + return $this->requestValidatorHelper->isValidForRedirect($this->request); } /** @@ -661,15 +479,15 @@ private function setValidRequest(GetResponseEvent $event) * * @throws \Exception */ - private function setNotEditableAwareMessage(GetResponseEvent $event) + protected function setNotEditableAwareMessage(GetResponseEvent $event) { //if document is root, no language tag is required if ($this->editmodeResolver->isEditmode()) { $response = new Response(); $language = 'en'; - if ($user = \Pimcore\Tool\Admin::getCurrentUser()) { + if ($user = Admin::getCurrentUser()) { $language = $user->getLanguage(); - } elseif ($user = \Pimcore\Tool\Authentication::authenticateSession()) { + } elseif ($user = Authentication::authenticateSession($event->getRequest())) { $language = $user->getLanguage(); } @@ -677,49 +495,30 @@ private function setNotEditableAwareMessage(GetResponseEvent $event) $event->setResponse($response); return; - } else { - $siteId = 1; - if (\Pimcore\Model\Site::isSiteRequest() === true) { - $site = \Pimcore\Model\Site::getCurrentSite(); - $siteId = $site->getRootId(); - } - - //if document is root, no language tag is required - if ($this->document->getId() !== $siteId) { - throw new \Exception(get_class($this->document) . ' (' . $this->document->getId() . ') does not have a valid language property!'); - } } - } - private function setDocumentLocale() - { - if ($this->document instanceof Document\Hardlink\Wrapper\WrapperInterface) { - /** @var Document\Hardlink\Wrapper $wrapperDocument */ - $wrapperDocument = $this->document; - $this->documentLocale = $wrapperDocument->getHardLinkSource()->getProperty('language'); - } else { - $this->documentLocale = $this->document->getProperty('language'); + $siteId = 1; + if (Site::isSiteRequest() === true) { + $site = Site::getCurrentSite(); + $siteId = $site->getRootId(); } - if ($this->i18nType === 'country') { - $this->documentCountry = Definitions::INTERNATIONAL_COUNTRY_NAMESPACE; + //if document is root, no language tag is required + if ($this->document->getId() !== $siteId) { + throw new \Exception(get_class($this->document) . ' (' . $this->document->getId() . ') does not have a valid language property!'); } + } - $this->documentLanguage = $this->documentLocale; + protected function setDocumentLocale($i18nType) + { + $documentLocaleData = $this->documentHelper->getDocumentLocaleData($this->document, $i18nType); - if (strpos($this->documentLocale, '_') !== false) { - $parts = explode('_', $this->documentLocale); - $this->documentLanguage = strtolower($parts[0]); - if (isset($parts[1]) && !empty($parts[1])) { - $this->documentCountry = strtoupper($parts[1]); - } - } + $this->documentLocale = $documentLocaleData['documentLocale']; + $this->documentLanguage = $documentLocaleData['documentLanguage']; + $this->documentCountry = $documentLocaleData['documentCountry']; } - /** - * Adjust Request locale. - */ - private function adjustRequestLocale() + protected function adjustRequestLocale() { // set request locale $this->request->attributes->set('_locale', $this->documentLocale); diff --git a/src/I18nBundle/EventListener/PimcoreRedirectListener.php b/src/I18nBundle/EventListener/PimcoreRedirectListener.php new file mode 100644 index 0000000..8096bb7 --- /dev/null +++ b/src/I18nBundle/EventListener/PimcoreRedirectListener.php @@ -0,0 +1,289 @@ +redirectorRegistry = $redirectorRegistry; + $this->zoneManager = $zoneManager; + $this->contextManager = $contextManager; + $this->pathGeneratorManager = $pathGeneratorManager; + $this->redirectHandler = $redirectHandler; + $this->siteResolver = $siteResolver; + $this->documentHelper = $documentHelper; + $this->requestValidatorHelper = $requestValidatorHelper; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return [ + KernelEvents::EXCEPTION => [['onKernelRedirectException', 65]], // before pimcore frontend routing listener (redirect check) + KernelEvents::REQUEST => [['onKernelRedirectRequest', 513]], // before pimcore frontend routing listener (redirect check) + ]; + } + + /** + * @param GetResponseForExceptionEvent $event + * + * @throws \Exception + */ + public function onKernelRedirectException(GetResponseForExceptionEvent $event) + { + if (!$event->getException() instanceof NotFoundHttpException) { + return; + } + + $response = $this->checkI18nPimcoreRedirects($event->getRequest()); + if ($response !== null) { + $event->setResponse($response); + } + } + + /** + * @param GetResponseEvent $event + * + * @throws \Exception + */ + public function onKernelRedirectRequest(GetResponseEvent $event) + { + if ($event->isMasterRequest() === false) { + return; + } + + if ($this->requestValidatorHelper->isValidForRedirect($event->getRequest(), false) === false) { + return; + } + + $this->resolveSite($event->getRequest()); + + $response = $this->checkI18nPimcoreRedirects($event->getRequest(), true); + + if ($response !== null) { + $event->setResponse($response); + } + } + + /** + * @param Request $request + * @param bool $override + * + * @return Response|null + * + * @throws \Exception + */ + protected function checkI18nPimcoreRedirects(Request $request, $override = false) + { + $response = $this->redirectHandler->checkForRedirect($request, $override); + if (!$response instanceof RedirectResponse) { + return null; + } + + $oldTargetUrl = $response->getTargetUrl(); + $oldTargetUrlParts = parse_url($oldTargetUrl); + + preg_match('/\{i18n_localized_target_page=([0-9]+)\}/', $response->getTargetUrl(), $matches); + + if (!is_array($matches) || count($matches) !== 2) { + return $response; + } + + $validDecision = null; + $documentId = $matches[1]; + $document = Document::getById($documentId); + + if (!$document instanceof Document) { + return $response; + } + + if (!$request->attributes->has('pimcore_request_source')) { + $request->attributes->set('pimcore_request_source', 'document'); + } + + $this->zoneManager->initZones(); + $this->contextManager->initContext($this->zoneManager->getCurrentZoneInfo('mode'), $document); + $this->pathGeneratorManager->initPathGenerator($request->attributes->get('pimcore_request_source')); + + $i18nType = $this->zoneManager->getCurrentZoneInfo('mode'); + $defaultLocale = $this->zoneManager->getCurrentZoneLocaleAdapter()->getDefaultLocale(); + $documentLocaleData = $this->documentHelper->getDocumentLocaleData($document, $i18nType); + + $options = [ + 'i18nType' => $i18nType, + 'request' => $request, + 'document' => $document, + 'documentLocale' => $documentLocaleData['documentLocale'], + 'documentCountry' => $documentLocaleData['documentCountry'], + 'defaultLocale' => $defaultLocale + ]; + + $redirectorBag = new RedirectorBag($options); + /** @var RedirectorInterface $redirector */ + foreach ($this->redirectorRegistry->all() as $redirector) { + // do not use redirector with storage functionality + if ($redirector instanceof CookieRedirector) { + continue; + } + + $redirector->makeDecision($redirectorBag); + $decision = $redirector->getDecision(); + + if ($decision['valid'] === true) { + $validDecision = $decision; + } + + $redirectorBag->addRedirectorDecisionToBag($redirector->getName(), $decision); + } + + if ($validDecision === null) { + $response->setTargetUrl($document->getFullPath()); + + return $response; + } + + $localizedUrls = $this->pathGeneratorManager->getPathGenerator()->getUrls($document, false); + + if (count($localizedUrls) === 0) { + $response->setTargetUrl($document->getFullPath()); + + return $response; + } + + $newTargetUrl = null; + foreach ($localizedUrls as $localizedUrl) { + if ($localizedUrl['locale'] === $validDecision['locale']) { + $newTargetUrl = $localizedUrl['url']; + } + } + + // no link found, use first one we've found. + if ($newTargetUrl === null) { + $response->setTargetUrl($localizedUrls[0]['url']); + + return $response; + } + + if (isset($oldTargetUrlParts['query']) && !empty($oldTargetUrlParts['query'])) { + $newTargetUrl .= strpos($newTargetUrl, '?') === false ? '?' : '&'; + $newTargetUrl .= $oldTargetUrlParts['query']; + } + + $response->setTargetUrl($newTargetUrl); + + return $response; + } + + /** + * @param Request $request + * + * @return string + */ + protected function resolveSite(Request $request) + { + $path = urldecode($request->getPathInfo()); + + if ($this->requestValidatorHelper->isFrontendRequestByAdmin($request)) { + return $path; + } + + $host = $request->getHost(); + $site = Site::getByDomain($host); + + if (!$site instanceof Site) { + return $path; + } + + $path = $site->getRootPath() . $path; + + Site::setCurrentSite($site); + + $this->siteResolver->setSite($request, $site); + $this->siteResolver->setSitePath($request, $path); + + return $path; + } +} diff --git a/src/I18nBundle/Helper/DocumentHelper.php b/src/I18nBundle/Helper/DocumentHelper.php index 7908ce4..67a9d5a 100644 --- a/src/I18nBundle/Helper/DocumentHelper.php +++ b/src/I18nBundle/Helper/DocumentHelper.php @@ -2,22 +2,28 @@ namespace I18nBundle\Helper; +use I18nBundle\Definitions; +use Pimcore\Model\Document; use Pimcore\Model\Site as PimcoreSite; +use Pimcore\Tool; +use Pimcore\Tool\Frontend; class DocumentHelper { /** * Get Documents Url and Path. * - * @param \Pimcore\Model\Document $document + * @param Document $document * * @return string + * + * @throws \Exception */ public function getDocumentFullPath($document = null) { - $hostUrl = \Pimcore\Tool::getHostUrl(); + $hostUrl = Tool::getHostUrl(); - if (!$document instanceof \Pimcore\Model\Document) { + if (!$document instanceof Document) { return $hostUrl; } @@ -29,6 +35,8 @@ public function getDocumentFullPath($document = null) /** * @return string + * + * @throws \Exception */ public function getCurrentPageRootPath() { @@ -41,45 +49,88 @@ public function getCurrentPageRootPath() return $rootPath; } + /** + * @param Document $document + * @param string $i18nType + * + * @return array + */ + public function getDocumentLocaleData(Document $document, $i18nType = 'language') + { + $documentLocale = null; + $documentLanguage = null; + $documentCountry = null; + + if ($document instanceof Document\Hardlink\Wrapper\WrapperInterface) { + /** @var Document\Hardlink\Wrapper $wrapperDocument */ + $wrapperDocument = $document; + $documentLocale = $wrapperDocument->getHardLinkSource()->getProperty('language'); + } else { + $documentLocale = $document->getProperty('language'); + } + + if ($i18nType === 'country') { + $documentCountry = Definitions::INTERNATIONAL_COUNTRY_NAMESPACE; + } + + $documentLanguage = $documentLocale; + + if (strpos($documentLocale, '_') !== false) { + $parts = explode('_', $documentLocale); + $documentLanguage = strtolower($parts[0]); + if (isset($parts[1]) && !empty($parts[1])) { + $documentCountry = strtoupper($parts[1]); + } + } + + return [ + 'documentLocale' => $documentLocale, + 'documentLanguage' => $documentLanguage, + 'documentCountry' => $documentCountry + ]; + } + /** * Get Documents Url without it's own path. * - * @param \Pimcore\Model\Document $document - * @param bool $returnAsArray - * @param bool $restrictToCurrentSite + * @param Document $document + * @param bool $returnAsArray + * @param bool $restrictToCurrentSite * * @return array string document url without trailing slash + * + * @throws \Exception */ - private function getDocumentUrl($document = null, $returnAsArray = false, $restrictToCurrentSite = true) + protected function getDocumentUrl($document = null, $returnAsArray = false, $restrictToCurrentSite = true) { $siteIsLanguageRoot = false; $url = ''; - if (!\Pimcore\Model\Site::isSiteRequest()) { - $url = rtrim(\Pimcore\Tool::getHostUrl(), '/'); + if (!PimcoreSite::isSiteRequest()) { + $url = rtrim(Tool::getHostUrl(), '/'); return $returnAsArray ? ['url' => $url, 'siteIsLanguageRoot' => $siteIsLanguageRoot] : $url; } if ($restrictToCurrentSite === false || $this->isDocumentInCurrentSite($document)) { - $site = \Pimcore\Model\Site::getCurrentSite(); + $site = PimcoreSite::getCurrentSite(); //we're in the current documents domain. add Host! - if ($site->getRootId() === $document->getId() || \Pimcore\Tool\Frontend::isDocumentInCurrentSite($document)) { + if ($site->getRootId() === $document->getId() || Frontend::isDocumentInCurrentSite($document)) { if ($site->getRootId() === $document->getId()) { $siteIsLanguageRoot = true; } - $hostUrl = \Pimcore\Tool::getHostUrl(); + $hostUrl = Tool::getHostUrl(); } else { - /** @var \Pimcore\Model\Site $documentSite */ - $documentSite = \Pimcore\Tool\Frontend::getSiteForDocument($document); + /** @var PimcoreSite $documentSite */ + $documentSite = Frontend::getSiteForDocument($document); if ($documentSite->getRootId() === $document->getId()) { $siteIsLanguageRoot = true; } - $hostUrl = \Pimcore\Tool::getRequestScheme() . '://' . $documentSite->getMainDomain(); + $hostUrl = Tool::getRequestScheme() . '://' . $documentSite->getMainDomain(); } $url = rtrim($hostUrl, '/'); @@ -99,19 +150,24 @@ private function getDocumentUrl($document = null, $returnAsArray = false, $restr * Checks if document is in current site. * also true if given document is actually current site. * - * @param \Pimcore\Model\Document $document + * @param Document $document * * @return bool + * + * @throws \Exception */ - private function isDocumentInCurrentSite($document) + protected function isDocumentInCurrentSite($document) { - if (!\Pimcore\Model\Site::isSiteRequest()) { + if (!PimcoreSite::isSiteRequest()) { return false; } - $site = \Pimcore\Model\Site::getCurrentSite(); + if (Frontend::isDocumentInCurrentSite($document)) { + return true; + } - if (\Pimcore\Tool\Frontend::isDocumentInCurrentSite($document) || $site->getRootId() === $document->getId()) { + $site = PimcoreSite::getCurrentSite(); + if ($site->getRootId() === $document->getId()) { return true; } diff --git a/src/I18nBundle/Helper/RequestValidatorHelper.php b/src/I18nBundle/Helper/RequestValidatorHelper.php new file mode 100644 index 0000000..75756cc --- /dev/null +++ b/src/I18nBundle/Helper/RequestValidatorHelper.php @@ -0,0 +1,78 @@ +requestHelper = $requestHelper; + $this->pimcoreContextResolver = $contextResolver; + } + + /** + * @param Request $request + * @param bool $allowFrontendRequestByAdmin + * + * @return bool + */ + public function isValidForRedirect(Request $request, $allowFrontendRequestByAdmin = true) + { + if ($this->pimcoreContextResolver->matchesPimcoreContext($request, PimcoreContextResolver::CONTEXT_ADMIN)) { + return false; + } + + if (!$this->pimcoreContextResolver->matchesPimcoreContext($request, PimcoreContextResolver::CONTEXT_DEFAULT)) { + return false; + } + + if ($allowFrontendRequestByAdmin === false && $this->requestHelper->isFrontendRequestByAdmin($request)) { + return false; + } + + if (System::isInCliMode()) { + return false; + } + } + + /** + * @param Request $request + * + * @return bool + */ + public function matchesDefaultPimcoreContext(Request $request) + { + return $this->pimcoreContextResolver->matchesPimcoreContext($request, PimcoreContextResolver::CONTEXT_DEFAULT); + } + + /** + * @param Request $request + * + * @return bool + */ + public function isFrontendRequestByAdmin(Request $request) + { + return $this->requestHelper->isFrontendRequestByAdmin($request); + } +} diff --git a/src/I18nBundle/Manager/ZoneManager.php b/src/I18nBundle/Manager/ZoneManager.php index 70d56c2..8407d4b 100644 --- a/src/I18nBundle/Manager/ZoneManager.php +++ b/src/I18nBundle/Manager/ZoneManager.php @@ -6,6 +6,7 @@ use I18nBundle\Configuration\Configuration; use I18nBundle\Definitions; use I18nBundle\Registry\LocaleRegistry; +use Pimcore\Db\Connection; use Pimcore\Http\Request\Resolver\DocumentResolver; use Pimcore\Http\Request\Resolver\EditmodeResolver; use Pimcore\Http\Request\Resolver\SiteResolver; @@ -15,6 +16,16 @@ class ZoneManager { + /** + * @var string|null + */ + protected $generalDomain; + + /** + * @var Connection + */ + protected $db; + /** * @var RequestHelper */ @@ -65,8 +76,8 @@ class ZoneManager protected $isInZone = false; /** - * ZoneManager constructor. - * + * @param string|null $generalDomain + * @param Connection $db * @param RequestHelper $requestHelper * @param SiteResolver $siteResolver * @param Configuration $configuration @@ -75,6 +86,8 @@ class ZoneManager * @param DocumentResolver $documentResolver */ public function __construct( + $generalDomain, + Connection $db, RequestHelper $requestHelper, SiteResolver $siteResolver, Configuration $configuration, @@ -82,6 +95,8 @@ public function __construct( EditmodeResolver $editmodeResolver, DocumentResolver $documentResolver ) { + $this->db = $db; + $this->generalDomain = $generalDomain; $this->requestHelper = $requestHelper; $this->siteResolver = $siteResolver; $this->configuration = $configuration; @@ -158,14 +173,13 @@ private function setupZoneDomains() return $this->currentZoneDomains; } - $db = \Pimcore\Db::get(); - $availableSites = $db->fetchAll('SELECT * FROM sites'); + $availableSites = $this->db->fetchAll('SELECT * FROM sites'); $zoneDomains = []; - //it's a simple page, no sites. if (empty($availableSites)) { - $zoneDomains[] = $this->mapDomainData(\Pimcore\Tool::getHostUrl(), 1); + $hostUrl = !empty($this->generalDomain) && $this->generalDomain !== 'localhost' ? $this->generalDomain : \Pimcore\Tool::getHostUrl(); + $zoneDomains[] = $this->mapDomainData($hostUrl, 1); } else { foreach ($availableSites as $site) { $domainInfo = $this->mapDomainData($site['mainDomain'], $site['rootId']); diff --git a/src/I18nBundle/Resources/config/services/event.yml b/src/I18nBundle/Resources/config/services/event.yml index 3818242..a389e62 100644 --- a/src/I18nBundle/Resources/config/services/event.yml +++ b/src/I18nBundle/Resources/config/services/event.yml @@ -5,12 +5,22 @@ services: autoconfigure: true public: false + # event: detect pimcore redirect + I18nBundle\EventListener\PimcoreRedirectListener: + tags: + - { name: kernel.event_subscriber } + # event: i18n detector, register language / country. setup all managers. redirect user I18nBundle\EventListener\DetectorListener: tags: - { name: kernel.event_subscriber } - # event: checks if hardlink page has a frontpage map + # event: i18n detector, register language / country. setup all managers. redirect user + I18nBundle\EventListener\ContextSwitchDetectorListener: + tags: + - { name: kernel.event_subscriber } + + # event: checks if hardlink page has a front-page map I18nBundle\EventListener\FrontPageMapperListener: tags: - { name: kernel.event_subscriber } diff --git a/src/I18nBundle/Resources/config/services/helper.yml b/src/I18nBundle/Resources/config/services/helper.yml index 0dd41b2..535168b 100644 --- a/src/I18nBundle/Resources/config/services/helper.yml +++ b/src/I18nBundle/Resources/config/services/helper.yml @@ -9,4 +9,6 @@ services: I18nBundle\Helper\CookieHelper: ~ - I18nBundle\Helper\UserHelper: ~ \ No newline at end of file + I18nBundle\Helper\UserHelper: ~ + + I18nBundle\Helper\RequestValidatorHelper: ~ \ No newline at end of file diff --git a/src/I18nBundle/Resources/config/services/manager.yml b/src/I18nBundle/Resources/config/services/manager.yml index c70ce2f..ae24eac 100644 --- a/src/I18nBundle/Resources/config/services/manager.yml +++ b/src/I18nBundle/Resources/config/services/manager.yml @@ -5,7 +5,9 @@ services: public: false # manager: zone - I18nBundle\Manager\ZoneManager: ~ + I18nBundle\Manager\ZoneManager: + arguments: + $generalDomain: '%router.request_context.host%' # manager: path_generator I18nBundle\Manager\PathGeneratorManager: ~ diff --git a/tests/_support/Helper/PimcoreBackend.php b/tests/_support/Helper/PimcoreBackend.php index 936d919..5242079 100644 --- a/tests/_support/Helper/PimcoreBackend.php +++ b/tests/_support/Helper/PimcoreBackend.php @@ -10,6 +10,7 @@ use Pimcore\Model\Document\Hardlink; use Pimcore\Model\Document\Page; use Pimcore\Model\Document\Service; +use Pimcore\Model\Redirect; use Pimcore\Model\Site; use Pimcore\Model\Staticroute; use Pimcore\Tests\Util\TestHelper; @@ -378,6 +379,22 @@ public function haveAStaticRoute(string $name, string $translationKey) return $route; } + /** + * Actor Function to generate a single pimcore redirect. + * + * @param array $data + * + * @return Redirect + */ + public function haveAPimcoreRedirect(array $data) + { + $redirect = new Redirect(); + $redirect->setValues($data); + $redirect->save(); + + return $redirect; + } + /** * API Function to create a page document * diff --git a/tests/_support/Util/I18nHelper.php b/tests/_support/Util/I18nHelper.php index 49b4b7c..6f79cb6 100644 --- a/tests/_support/Util/I18nHelper.php +++ b/tests/_support/Util/I18nHelper.php @@ -3,6 +3,7 @@ namespace DachcomBundle\Test\Util; use Pimcore\Model\Document; +use Pimcore\Model\Redirect; use Pimcore\Tests\Util\TestHelper; class I18nHelper @@ -29,5 +30,11 @@ public static function cleanUp() $db->delete('sites', ['id' => $availableSite['id']]); } } + + // remove all redirects + $redirects = new Redirect\Listing(); + foreach ($redirects->load() as $redirect) { + $redirect->delete(); + } } } diff --git a/tests/functional.zone.extended/PimcoreRedirectCest.php b/tests/functional.zone.extended/PimcoreRedirectCest.php new file mode 100644 index 0000000..fde759e --- /dev/null +++ b/tests/functional.zone.extended/PimcoreRedirectCest.php @@ -0,0 +1,71 @@ +setupSites($I); + + $document1 = $I->haveASubPageDocument($data['document1'], 'about-us'); + $document2 = $I->haveASubPageDocument($data['document2'], 'ueber-uns'); + $document3 = $I->haveASubPageDocument($data['document3'], 'riguardo-a-noi'); + $document4 = $I->haveASubPageDocument($data['site3']->getRootDocument(), 'propos-de-nous'); + + $I->haveTwoConnectedDocuments($document1, $document2); + $I->haveTwoConnectedDocuments($document1, $document3); + $I->haveTwoConnectedDocuments($document1, $document4); + + $redirect = [ + 'type' => 'path', + 'source' => '@^/my-special-redirect@', + 'sourceSite' => $data['site1']->getId(), + 'target' => sprintf('/{i18n_localized_target_page=%s}', $document3->getId()), + 'targetSite' => $data['site1']->getId(), + 'statusCode' => 301, + 'regex' => 1, + ]; + + $I->haveAPimcoreRedirect($redirect); + $I->amOnPageWithLocale('http://test-domain1.test/my-special-redirect', 'it'); + $I->seeCurrentUrlEquals('/it/riguardo-a-noi'); + } + + /** + * @param FunctionalTester $I + */ + public function testExtendedLocalizedRedirectPathNotFound(FunctionalTester $I) + { + $data = $this->setupSites($I); + + $document1 = $I->haveASubPageDocument($data['document1'], 'about-us'); + $document2 = $I->haveASubPageDocument($data['document2'], 'ueber-uns'); + $document3 = $I->haveASubPageDocument($data['document3'], 'riguardo-a-noi'); + $document4 = $I->haveASubPageDocument($data['site3']->getRootDocument(), 'propos-de-nous'); + + $I->haveTwoConnectedDocuments($document1, $document2); + $I->haveTwoConnectedDocuments($document1, $document3); + $I->haveTwoConnectedDocuments($document1, $document4); + + $redirect = [ + 'type' => 'path', + 'source' => '@^/my-special-redirect@', + 'sourceSite' => null, + 'target' => sprintf('/{i18n_localized_target_page=%s}', $document3->getId()), + 'targetSite' => null, + 'statusCode' => 301, + 'regex' => 1, + ]; + + $I->haveAPimcoreRedirect($redirect); + $I->amOnPageWithLocale('http://test-domain1.test/my-special-redirect', 'it'); + $I->seeCurrentUrlEquals('/my-special-redirect'); + $I->canSeePageNotFound(); + } +} \ No newline at end of file diff --git a/tests/functional.zone.simple/PimcoreRedirectCest.php b/tests/functional.zone.simple/PimcoreRedirectCest.php new file mode 100644 index 0000000..3165b66 --- /dev/null +++ b/tests/functional.zone.simple/PimcoreRedirectCest.php @@ -0,0 +1,63 @@ +haveASite('test-domain1.test'); + + $document1 = $I->haveAPageDocumentForSite($site1, 'en', 'en'); + $document2 = $I->haveAPageDocumentForSite($site1, 'de', 'de'); + + $redirect = [ + 'type' => 'path', + 'source' => '@^/my-special-redirect-uri@', + 'sourceSite' => $site1->getId(), + 'target' => sprintf('/{i18n_localized_target_page=%s}', $document2->getId()), + 'targetSite' => null, + 'statusCode' => 301, + 'regex' => 1, + ]; + + $I->haveAPimcoreRedirect($redirect); + $I->amOnPageWithLocale('http://test-domain1.test/my-special-redirect-uri', 'de'); + $I->seeCurrentUrlEquals('/de'); + + } + + /** + * @param FunctionalTester $I + */ + public function testLocalizedRedirectEntireUri(FunctionalTester $I) + { + $site1 = $I->haveASite('test-domain1.test'); + + $document1 = $I->haveAPageDocumentForSite($site1, 'en', 'en'); + $document2 = $I->haveAPageDocumentForSite($site1, 'de', 'de'); + + $redirect = [ + 'type' => 'entire_uri', + 'source' => '@https?://test-domain3\.test@', + 'sourceSite' => null, + 'target' => sprintf('/{i18n_localized_target_page=%s}', $document2->getId()), + 'targetSite' => null, + 'statusCode' => 301, + 'regex' => 1, + 'priority' => 99 + ]; + + $I->haveAPimcoreRedirect($redirect); + $I->amOnPageWithLocale('http://test-domain3.test', 'de'); + + $I->seeCurrentHostEquals('test-domain1.test'); + $I->seeCurrentUrlEquals('/de'); + + } +} \ No newline at end of file