diff --git a/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php b/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php index ff49a20427f8c..849952eae6218 100644 --- a/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php +++ b/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php @@ -66,26 +66,28 @@ public function aroundDispatch( \Closure $proceed, \Magento\Framework\App\RequestInterface $request ) { - if (!$request->isPost() && $this->getBaseUrlChecker()->isEnabled()) { + if ($this->getBaseUrlChecker()->isEnabled()) { $baseUrl = $this->_storeManager->getStore()->getBaseUrl( \Magento\Framework\UrlInterface::URL_TYPE_WEB, $this->_storeManager->getStore()->isCurrentlySecure() ); if ($baseUrl) { $uri = parse_url($baseUrl); - if (!$this->getBaseUrlChecker()->execute($uri, $request)) { - $redirectUrl = $this->_url->getRedirectUrl( - $this->_url->getUrl(ltrim($request->getPathInfo(), '/'), ['_nosid' => true]) - ); - $redirectCode = (int)$this->_scopeConfig->getValue( - 'web/url/redirect_to_base', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) !== 301 ? 302 : 301; + if (!$request->isPost()) { + if (!$this->getBaseUrlChecker()->execute($uri, $request)) { + $redirectUrl = $this->_url->getRedirectUrl( + $this->_url->getUrl(ltrim($request->getPathInfo(), '/'), ['_nosid' => true]) + ); + $redirectCode = (int)$this->_scopeConfig->getValue( + 'web/url/redirect_to_base', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ) !== 301 ? 302 : 301; - $response = $this->_responseFactory->create(); - $response->setRedirect($redirectUrl, $redirectCode); - $response->setNoCacheHeaders(); - return $response; + $response = $this->_responseFactory->create(); + $response->setRedirect($redirectUrl, $redirectCode); + $response->setNoCacheHeaders(); + return $response; + } } } } diff --git a/app/code/Magento/Store/App/Http/Plugin/StoreHttp.php b/app/code/Magento/Store/App/Http/Plugin/StoreHttp.php new file mode 100644 index 0000000000000..6c0f7ada75ead --- /dev/null +++ b/app/code/Magento/Store/App/Http/Plugin/StoreHttp.php @@ -0,0 +1,70 @@ +_storeManager = $storeManager; + $this->_url = $url; + $this->_request = $request; + } + + /** + * Update the request's pathInfo if we have a store on a url path + * + * This will make sure we match the correct area and so the requested url + * will be routed via the correct path. + * + * @return void + */ + public function beforeLaunch() + { + $baseUrl = $this->_storeManager->getStore()->getBaseUrl( + UrlInterface::URL_TYPE_WEB, + $this->_storeManager->getStore()->isCurrentlySecure() + ); + if ($baseUrl) { + $uri = parse_url($baseUrl); + if (isset($uri['path']) && '/' !== $uri['path']) { + $newPath = str_replace( + $uri['path'], + '', + $this->_request->getPathInfo() + ); + if ('/' !== substr($newPath, 0, 1)) { + $newPath = '/' . $newPath; + } + $this->_request->setPathInfo($newPath); + } + } + } +} diff --git a/app/code/Magento/Store/App/Response/Redirect.php b/app/code/Magento/Store/App/Response/Redirect.php index f214d9ce4369f..f2afdf15b5958 100644 --- a/app/code/Magento/Store/App/Response/Redirect.php +++ b/app/code/Magento/Store/App/Response/Redirect.php @@ -8,6 +8,7 @@ namespace Magento\Store\App\Response; use Magento\Store\Api\StoreResolverInterface; +use Magento\Store\Api\Data\StoreInterface; class Redirect implements \Magento\Framework\App\Response\RedirectInterface { @@ -208,13 +209,111 @@ protected function _isUrlInternal($url) { if (strpos($url, 'http') !== false) { $directLinkType = \Magento\Framework\UrlInterface::URL_TYPE_DIRECT_LINK; - $unsecureBaseUrl = $this->_storeManager->getStore()->getBaseUrl($directLinkType, false); - $secureBaseUrl = $this->_storeManager->getStore()->getBaseUrl($directLinkType, true); - return (strpos($url, $unsecureBaseUrl) === 0) || (strpos($url, $secureBaseUrl) === 0); + $currentStore = $this->_storeManager->getStore(); + $currentStoreUnsecureBaseUrl = $currentStore + ->getBaseUrl($directLinkType, false); + $currentStoreSecureBaseUrl = $currentStore + ->getBaseUrl($directLinkType, true); + $internal = ( + strpos($url, $currentStoreUnsecureBaseUrl) === 0 + || strpos($url, $currentStoreSecureBaseUrl) === 0 + ); + if (true === $internal) { + $internal = ! $this->_isUrlFromOtherStore($url, $currentStore); + } + return $internal; } return false; } + /** + * check if an 'internal' url is coming from another store + * + * @param string $url + * @param \Magento\Store\Api\Data\StoreInterface $currentStore + * @return bool + */ + protected function _isUrlFromOtherStore($url, \Magento\Store\Api\Data\StoreInterface $currentStore) + { + /** + * check if the referer belongs to another storeview + */ + $linkFromOtherStoreview = false; + $stores = $this->_storeManager->getStores(true); + foreach ($stores as $store) { + if ($currentStore->getId() !== $store->getId()) { + $isChildStore = $this->_checkStoreIsChildOfCurrentStore( + $url, + $currentStore, + $store + ); + if (true === $isChildStore + && false === $linkFromOtherStoreview) { + $linkFromOtherStoreview = true; + } + } + } + return $linkFromOtherStoreview; + } + + /** + * check if a store has a 'child' url of the current store + * + * example: + * - $currentStore has url https://store.com/ + * - $store has url https://store.com/nl/ + * Here we should return true since the $store's url is just adding an + * extra path to the $currentStore. + * + * @param string $url + * @param \Magento\Store\Api\Data\StoreInterface $currentStore + * @param \Magento\Store\Api\Data\StoreInterface $store + * @return bool + */ + protected function _checkStoreIsChildOfCurrentStore( + $url, + \Magento\Store\Api\Data\StoreInterface $currentStore, + \Magento\Store\Api\Data\StoreInterface $store + ) { + $directLinkType = \Magento\Framework\UrlInterface::URL_TYPE_DIRECT_LINK; + + $currentStoreUnsecureBaseUrl = $currentStore + ->getBaseUrl($directLinkType, false); + $currentStoreSecureBaseUrl = $currentStore + ->getBaseUrl($directLinkType, true); + + $unsecureBaseUrl = $store + ->getBaseUrl($directLinkType, false); + $secureBaseUrl = $store + ->getBaseUrl($directLinkType, true); + // does the beginning of the url matches another + // store in this system + // indicating the other store is just an additional + // item in the url path + // + // example: + // basestore = http://example.com/ + // german store = http://example.com/de/ + $canMatchOtherStoreView = ( + strpos($url, $unsecureBaseUrl) === 0 + || strpos($url, $secureBaseUrl) === 0 + ); + // check if the other store's baseurl could be the + // 'parent' of the current stores baseurl unless the + // baseurl is exactly the same. + $otherStoreViewSubCurrentStore = ( + (strpos($unsecureBaseUrl, $currentStoreUnsecureBaseUrl) === 0 + || strpos($secureBaseUrl, $currentStoreSecureBaseUrl) === 0) + && ($currentStoreUnsecureBaseUrl !== $unsecureBaseUrl + && $currentStoreSecureBaseUrl !== $secureBaseUrl) + ); + // when the url can also match the other store + // and the other store's baseurl is a 'child' of the + // current store's baseurl state the link is coming + // from external (other store) + return $canMatchOtherStoreView && $otherStoreViewSubCurrentStore; + } + /** * Normalize path to avoid wrong store change * diff --git a/app/code/Magento/Store/Block/Switcher.php b/app/code/Magento/Store/Block/Switcher.php index d5560b857a2c8..540f4a72a330a 100644 --- a/app/code/Magento/Store/Block/Switcher.php +++ b/app/code/Magento/Store/Block/Switcher.php @@ -221,7 +221,7 @@ public function getTargetStorePostData(\Magento\Store\Model\Store $store, $data { $data[\Magento\Store\Api\StoreResolverInterface::PARAM_NAME] = $store->getCode(); return $this->_postDataHelper->getPostData( - $this->getUrl('stores/store/switch'), + $this->getUrl('stores/store/switch', ['_scope' => $store]), $data ); } diff --git a/app/code/Magento/Store/Test/Unit/App/Response/RedirectTest.php b/app/code/Magento/Store/Test/Unit/App/Response/RedirectTest.php index 18b303763a5ac..f8769c23c48ce 100644 --- a/app/code/Magento/Store/Test/Unit/App/Response/RedirectTest.php +++ b/app/code/Magento/Store/Test/Unit/App/Response/RedirectTest.php @@ -82,6 +82,8 @@ public function testSuccessUrl($baseUrl, $successUrl) $this->_requestMock->expects($this->any())->method('getParam')->will($this->returnValue(null)); $this->_storeManagerMock->expects($this->any())->method('getStore') ->will($this->returnValue($testStoreMock)); + $this->_storeManagerMock->expects($this->any())->method('getStores') + ->will($this->returnValue([$testStoreMock])); $this->assertEquals($baseUrl, $this->_model->success($successUrl)); } diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml index 3e9192c24d533..8b44d69585957 100644 --- a/app/code/Magento/Store/etc/di.xml +++ b/app/code/Magento/Store/etc/di.xml @@ -24,6 +24,9 @@ + + +