1
|
|
|
<?php declare(strict_types=1); |
2
|
|
|
|
3
|
|
|
namespace Shopware\Storefront\Framework\Routing; |
4
|
|
|
|
5
|
|
|
use Shopware\Core\Checkout\Cart\Exception\CustomerNotLoggedInException; |
6
|
|
|
use Shopware\Core\Checkout\Customer\Event\CustomerLoginEvent; |
7
|
|
|
use Shopware\Core\Checkout\Customer\Event\CustomerLogoutEvent; |
8
|
|
|
use Shopware\Core\Content\Seo\HreflangLoaderInterface; |
9
|
|
|
use Shopware\Core\Content\Seo\HreflangLoaderParameter; |
10
|
|
|
use Shopware\Core\Framework\App\ActiveAppsLoader; |
11
|
|
|
use Shopware\Core\Framework\App\Exception\AppUrlChangeDetectedException; |
12
|
|
|
use Shopware\Core\Framework\App\ShopId\ShopIdProvider; |
13
|
|
|
use Shopware\Core\Framework\Event\BeforeSendResponseEvent; |
14
|
|
|
use Shopware\Core\Framework\Feature; |
15
|
|
|
use Shopware\Core\Framework\Routing\Annotation\RouteScope; |
16
|
|
|
use Shopware\Core\Framework\Routing\KernelListenerPriorities; |
17
|
|
|
use Shopware\Core\Framework\Util\Random; |
18
|
|
|
use Shopware\Core\PlatformRequest; |
19
|
|
|
use Shopware\Core\SalesChannelRequest; |
20
|
|
|
use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceInterface; |
21
|
|
|
use Shopware\Core\System\SalesChannel\SalesChannelContext; |
22
|
|
|
use Shopware\Storefront\Controller\ErrorController; |
23
|
|
|
use Shopware\Storefront\Event\StorefrontRenderEvent; |
24
|
|
|
use Shopware\Storefront\Framework\Csrf\CsrfPlaceholderHandler; |
25
|
|
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface; |
26
|
|
|
use Symfony\Component\HttpFoundation\RedirectResponse; |
27
|
|
|
use Symfony\Component\HttpFoundation\RequestStack; |
28
|
|
|
use Symfony\Component\HttpKernel\Event\ControllerEvent; |
29
|
|
|
use Symfony\Component\HttpKernel\Event\ExceptionEvent; |
30
|
|
|
use Symfony\Component\HttpKernel\Event\RequestEvent; |
31
|
|
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; |
32
|
|
|
use Symfony\Component\HttpKernel\KernelEvents; |
33
|
|
|
use Symfony\Component\Routing\RouterInterface; |
34
|
2 |
|
|
35
|
|
|
class StorefrontSubscriber implements EventSubscriberInterface |
36
|
2 |
|
{ |
37
|
2 |
|
/** |
38
|
2 |
|
* @var RequestStack |
39
|
2 |
|
*/ |
40
|
|
|
private $requestStack; |
41
|
2 |
|
|
42
|
|
|
/** |
43
|
|
|
* @var RouterInterface |
44
|
2 |
|
*/ |
45
|
|
|
private $router; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var ErrorController |
49
|
|
|
*/ |
50
|
|
|
private $errorController; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @var SalesChannelContextServiceInterface |
54
|
201 |
|
*/ |
55
|
|
|
private $contextService; |
56
|
201 |
|
|
57
|
201 |
|
/** |
58
|
|
|
* @var bool |
59
|
|
|
*/ |
60
|
201 |
|
private $kernelDebug; |
61
|
201 |
|
|
62
|
|
|
/** |
63
|
|
|
* @var CsrfPlaceholderHandler |
64
|
|
|
*/ |
65
|
|
|
private $csrfPlaceholderHandler; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* @var MaintenanceModeResolver |
69
|
|
|
*/ |
70
|
|
|
private $maintenanceModeResolver; |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* @var HreflangLoaderInterface |
74
|
|
|
*/ |
75
|
|
|
private $hreflangLoader; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* @var ShopIdProvider |
79
|
|
|
*/ |
80
|
|
|
private $shopIdProvider; |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* @var ActiveAppsLoader |
84
|
|
|
*/ |
85
|
|
|
private $activeAppsLoader; |
86
|
|
|
|
87
|
|
|
public function __construct( |
88
|
|
|
RequestStack $requestStack, |
89
|
|
|
RouterInterface $router, |
90
|
|
|
ErrorController $errorController, |
91
|
|
|
SalesChannelContextServiceInterface $contextService, |
92
|
|
|
CsrfPlaceholderHandler $csrfPlaceholderHandler, |
93
|
|
|
HreflangLoaderInterface $hreflangLoader, |
94
|
|
|
bool $kernelDebug, |
95
|
|
|
MaintenanceModeResolver $maintenanceModeResolver, |
96
|
|
|
ShopIdProvider $shopIdProvider, |
97
|
57 |
|
ActiveAppsLoader $activeAppsLoader |
98
|
|
|
) { |
99
|
57 |
|
$this->requestStack = $requestStack; |
100
|
57 |
|
$this->router = $router; |
101
|
|
|
$this->errorController = $errorController; |
102
|
|
|
$this->contextService = $contextService; |
103
|
|
|
$this->kernelDebug = $kernelDebug; |
104
|
|
|
$this->csrfPlaceholderHandler = $csrfPlaceholderHandler; |
105
|
|
|
$this->maintenanceModeResolver = $maintenanceModeResolver; |
106
|
|
|
$this->hreflangLoader = $hreflangLoader; |
107
|
|
|
$this->shopIdProvider = $shopIdProvider; |
108
|
|
|
$this->activeAppsLoader = $activeAppsLoader; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
public static function getSubscribedEvents(): array |
112
|
|
|
{ |
113
|
|
|
return [ |
114
|
|
|
KernelEvents::REQUEST => [ |
115
|
|
|
['startSession', 40], |
116
|
|
|
['maintenanceResolver'], |
117
|
|
|
], |
118
|
|
|
KernelEvents::EXCEPTION => [ |
119
|
|
|
['showHtmlExceptionResponse', -100], |
120
|
|
|
['customerNotLoggedInHandler'], |
121
|
|
|
['maintenanceResolver'], |
122
|
|
|
], |
123
|
|
|
KernelEvents::CONTROLLER => [ |
124
|
|
|
['preventPageLoadingFromXmlHttpRequest', KernelListenerPriorities::KERNEL_CONTROLLER_EVENT_SCOPE_VALIDATE], |
125
|
|
|
], |
126
|
|
|
CustomerLoginEvent::class => [ |
127
|
|
|
'updateSessionAfterLogin', |
128
|
|
|
], |
129
|
|
|
CustomerLogoutEvent::class => [ |
130
|
|
|
'updateSessionAfterLogout', |
131
|
|
|
], |
132
|
|
|
BeforeSendResponseEvent::class => [ |
133
|
|
|
['replaceCsrfToken'], |
134
|
|
|
['setCanonicalUrl'], |
135
|
|
|
], |
136
|
|
|
StorefrontRenderEvent::class => [ |
137
|
|
|
['addHreflang'], |
138
|
|
|
['addShopIdParameter'], |
139
|
|
|
], |
140
|
|
|
]; |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
public function startSession(): void |
144
|
|
|
{ |
145
|
|
|
$master = $this->requestStack->getMasterRequest(); |
146
|
|
|
|
147
|
|
|
if (!$master) { |
148
|
|
|
return; |
149
|
|
|
} |
150
|
|
|
if (!$master->attributes->get(SalesChannelRequest::ATTRIBUTE_IS_SALES_CHANNEL_REQUEST)) { |
151
|
|
|
return; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
if (!$master->hasSession()) { |
155
|
|
|
return; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
$session = $master->getSession(); |
159
|
|
|
$applicationId = $master->attributes->get(PlatformRequest::ATTRIBUTE_OAUTH_CLIENT_ID); |
160
|
|
|
|
161
|
|
|
if (!$session->isStarted()) { |
162
|
|
|
$session->setName('session-' . $applicationId); |
163
|
|
|
$session->start(); |
164
|
|
|
$session->set('sessionId', $session->getId()); |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
$salesChannelId = $master->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID); |
168
|
|
|
if ($salesChannelId === null) { |
169
|
|
|
/** @var SalesChannelContext|null $salesChannelContext */ |
170
|
|
|
$salesChannelContext = $master->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT); |
171
|
|
|
if ($salesChannelContext !== null) { |
172
|
|
|
$salesChannelId = $salesChannelContext->getSalesChannel()->getId(); |
173
|
|
|
} |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
if (!$session->has(PlatformRequest::HEADER_CONTEXT_TOKEN) || $session->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID) !== $salesChannelId) { |
177
|
|
|
$token = Random::getAlphanumericString(32); |
178
|
|
|
$session->set(PlatformRequest::HEADER_CONTEXT_TOKEN, $token); |
179
|
|
|
$session->set(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID, $salesChannelId); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
$master->headers->set( |
183
|
|
|
PlatformRequest::HEADER_CONTEXT_TOKEN, |
184
|
|
|
$session->get(PlatformRequest::HEADER_CONTEXT_TOKEN) |
185
|
|
|
); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
public function updateSessionAfterLogin(CustomerLoginEvent $event): void |
189
|
|
|
{ |
190
|
|
|
$token = $event->getContextToken(); |
191
|
|
|
|
192
|
|
|
$this->updateSession($token); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
public function updateSessionAfterLogout(CustomerLogoutEvent $event): void |
196
|
|
|
{ |
197
|
|
|
if (!Feature::isActive('FEATURE_NEXT_10058')) { |
198
|
|
|
return; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
$newToken = $event->getSalesChannelContext()->getToken(); |
202
|
|
|
|
203
|
|
|
$this->updateSession($newToken); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
public function updateSession(string $token): void |
207
|
|
|
{ |
208
|
|
|
$master = $this->requestStack->getMasterRequest(); |
209
|
|
|
if (!$master) { |
210
|
|
|
return; |
211
|
|
|
} |
212
|
|
|
if (!$master->attributes->get(SalesChannelRequest::ATTRIBUTE_IS_SALES_CHANNEL_REQUEST)) { |
213
|
|
|
return; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
if (!$master->hasSession()) { |
217
|
|
|
return; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
$session = $master->getSession(); |
221
|
|
|
$session->migrate(); |
222
|
|
|
$session->set('sessionId', $session->getId()); |
223
|
|
|
|
224
|
|
|
$session->set(PlatformRequest::HEADER_CONTEXT_TOKEN, $token); |
225
|
|
|
$master->headers->set(PlatformRequest::HEADER_CONTEXT_TOKEN, $token); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
public function showHtmlExceptionResponse(ExceptionEvent $event): void |
229
|
|
|
{ |
230
|
|
|
if ($this->kernelDebug) { |
231
|
|
|
return; |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
if (!$event->getRequest()->attributes->has(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT)) { |
235
|
|
|
//When no saleschannel context is resolved, we need to resolve it now. |
236
|
|
|
$this->setSalesChannelContext($event); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
if ($event->getRequest()->attributes->has(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT)) { |
240
|
|
|
$event->stopPropagation(); |
|
|
|
|
241
|
|
|
$response = $this->errorController->error( |
242
|
|
|
$event->getThrowable(), |
243
|
|
|
$this->requestStack->getMasterRequest(), |
|
|
|
|
244
|
|
|
$event->getRequest()->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT) |
|
|
|
|
245
|
|
|
); |
246
|
|
|
$event->setResponse($response); |
247
|
|
|
} |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
public function customerNotLoggedInHandler(ExceptionEvent $event): void |
251
|
|
|
{ |
252
|
|
|
if (!$event->getRequest()->attributes->has(SalesChannelRequest::ATTRIBUTE_IS_SALES_CHANNEL_REQUEST)) { |
253
|
|
|
return; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
if (!$event->getThrowable() instanceof CustomerNotLoggedInException) { |
257
|
|
|
return; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
$request = $event->getRequest(); |
261
|
|
|
|
262
|
|
|
$parameters = [ |
263
|
|
|
'redirectTo' => $request->attributes->get('_route'), |
264
|
|
|
'redirectParameters' => json_encode($request->attributes->get('_route_params')), |
265
|
|
|
]; |
266
|
|
|
|
267
|
|
|
$redirectResponse = new RedirectResponse($this->router->generate('frontend.account.login.page', $parameters)); |
268
|
|
|
|
269
|
|
|
$event->setResponse($redirectResponse); |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
public function maintenanceResolver(RequestEvent $event): void |
273
|
|
|
{ |
274
|
|
|
if ($this->maintenanceModeResolver->shouldRedirect($event->getRequest())) { |
275
|
|
|
$event->setResponse( |
276
|
|
|
new RedirectResponse( |
277
|
|
|
$this->router->generate('frontend.maintenance.page'), |
278
|
|
|
RedirectResponse::HTTP_TEMPORARY_REDIRECT |
279
|
|
|
) |
280
|
|
|
); |
281
|
|
|
} |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
public function preventPageLoadingFromXmlHttpRequest(ControllerEvent $event): void |
285
|
|
|
{ |
286
|
|
|
if (!$event->getRequest()->isXmlHttpRequest()) { |
287
|
|
|
return; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
/** @var RouteScope $scope */ |
291
|
|
|
$scope = $event->getRequest()->attributes->get(PlatformRequest::ATTRIBUTE_ROUTE_SCOPE, new RouteScope(['scopes' => []])); |
292
|
|
|
if (!$scope->hasScope(StorefrontRouteScope::ID)) { |
293
|
|
|
return; |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
$controller = $event->getController(); |
297
|
|
|
|
298
|
|
|
// happens if Controller is a closure |
299
|
|
|
if (!is_array($controller)) { |
300
|
|
|
return; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
$isAllowed = $event->getRequest()->attributes->getBoolean('XmlHttpRequest', false); |
304
|
|
|
|
305
|
|
|
if ($isAllowed) { |
306
|
|
|
return; |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
throw new AccessDeniedHttpException('PageController can\'t be requested via XmlHttpRequest.'); |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
public function setCanonicalUrl(BeforeSendResponseEvent $event): void |
313
|
|
|
{ |
314
|
|
|
if (!$event->getResponse()->isSuccessful()) { |
315
|
|
|
return; |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
if ($canonical = $event->getRequest()->attributes->get(SalesChannelRequest::ATTRIBUTE_CANONICAL_LINK)) { |
319
|
|
|
$canonical = sprintf('<%s>; rel="canonical"', $canonical); |
320
|
|
|
$event->getResponse()->headers->set('Link', $canonical); |
321
|
|
|
} |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
public function replaceCsrfToken(BeforeSendResponseEvent $event): void |
325
|
|
|
{ |
326
|
|
|
$event->setResponse( |
327
|
|
|
$this->csrfPlaceholderHandler->replaceCsrfToken($event->getResponse(), $event->getRequest()) |
328
|
|
|
); |
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
public function addHreflang(StorefrontRenderEvent $event): void |
332
|
|
|
{ |
333
|
|
|
$request = $event->getRequest(); |
334
|
|
|
$route = $request->attributes->get('_route'); |
335
|
|
|
|
336
|
|
|
if ($route === null) { |
337
|
|
|
return; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
$routeParams = $request->attributes->get('_route_params', []); |
341
|
|
|
$salesChannelContext = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT); |
342
|
|
|
$parameter = new HreflangLoaderParameter($route, $routeParams, $salesChannelContext); |
343
|
|
|
$event->setParameter('hrefLang', $this->hreflangLoader->load($parameter)); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
public function addShopIdParameter(StorefrontRenderEvent $event): void |
347
|
|
|
{ |
348
|
|
|
if (!$this->activeAppsLoader->getActiveApps()) { |
349
|
|
|
return; |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
try { |
353
|
|
|
$shopId = $this->shopIdProvider->getShopId(); |
354
|
|
|
} catch (AppUrlChangeDetectedException $e) { |
355
|
|
|
return; |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
$event->setParameter('appShopId', $shopId); |
359
|
|
|
/* |
360
|
|
|
* @deprecated tag:v6.4.0 use `appShopId` instead |
361
|
|
|
*/ |
362
|
|
|
$event->setParameter('swagShopId', $shopId); |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
private function setSalesChannelContext(ExceptionEvent $event): void |
366
|
|
|
{ |
367
|
|
|
$contextToken = $event->getRequest()->headers->get(PlatformRequest::HEADER_CONTEXT_TOKEN); |
368
|
|
|
$salesChannelId = $event->getRequest()->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID); |
369
|
|
|
|
370
|
|
|
$context = $this->contextService->get( |
|
|
|
|
371
|
|
|
$salesChannelId, |
372
|
|
|
$contextToken, |
373
|
|
|
$event->getRequest()->headers->get(PlatformRequest::HEADER_LANGUAGE_ID), |
374
|
|
|
$event->getRequest()->attributes->get(SalesChannelRequest::ATTRIBUTE_DOMAIN_CURRENCY_ID) |
|
|
|
|
375
|
|
|
); |
376
|
|
|
$event->getRequest()->attributes->set(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT, $context); |
377
|
|
|
} |
378
|
|
|
} |
379
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.