diff --git a/composer.json b/composer.json
index 128e525ad5..7950aa9c85 100644
--- a/composer.json
+++ b/composer.json
@@ -126,6 +126,9 @@
"allow-plugins": {
"composer/package-versions-deprecated": true,
"*": false
+ },
+ "audit": {
+ "ignore": ["PKSA-w9tt-7782-78jx"]
}
},
"scripts": {
diff --git a/dependencies.json b/dependencies.json
new file mode 100644
index 0000000000..b18c7f710a
--- /dev/null
+++ b/dependencies.json
@@ -0,0 +1,11 @@
+{
+ "recipesEndpoint": "",
+ "packages": [
+ {
+ "requirement": "dev-IBX-12204-siteaccessservice-scope-stack as 6.0.x-dev",
+ "repositoryUrl": "https://github.com/ibexa/behat.git",
+ "package": "ibexa/behat",
+ "shouldBeAddedAsVCS": false
+ }
+ ]
+}
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index fab0bda625..0c1d92a7d2 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -1596,18 +1596,6 @@ parameters:
count: 1
path: src/bundle/Core/EventListener/ConfigScopeListener.php
- -
- message: '#^Cannot access property \$matchingType on Ibexa\\Core\\MVC\\Symfony\\SiteAccess\|null\.$#'
- identifier: property.nonObject
- count: 2
- path: src/bundle/Core/EventListener/ConsoleCommandListener.php
-
- -
- message: '#^Cannot access property \$name on Ibexa\\Core\\MVC\\Symfony\\SiteAccess\|null\.$#'
- identifier: property.nonObject
- count: 3
- path: src/bundle/Core/EventListener/ConsoleCommandListener.php
-
-
message: '#^Method Ibexa\\Bundle\\Core\\EventListener\\ConsoleCommandListener\:\:onConsoleCommand\(\) has no return type specified\.$#'
identifier: missingType.return
@@ -1626,18 +1614,6 @@ parameters:
count: 1
path: src/bundle/Core/EventListener/ConsoleCommandListener.php
- -
- message: '#^Method Ibexa\\Bundle\\Core\\EventListener\\ConsoleCommandListener\:\:setSiteAccess\(\) has no return type specified\.$#'
- identifier: missingType.return
- count: 1
- path: src/bundle/Core/EventListener/ConsoleCommandListener.php
-
- -
- message: '#^Parameter \#1 \$siteAccess of class Ibexa\\Core\\MVC\\Symfony\\Event\\ScopeChangeEvent constructor expects Ibexa\\Core\\MVC\\Symfony\\SiteAccess, Ibexa\\Core\\MVC\\Symfony\\SiteAccess\|null given\.$#'
- identifier: argument.type
- count: 1
- path: src/bundle/Core/EventListener/ConsoleCommandListener.php
-
-
message: '#^Method Ibexa\\Bundle\\Core\\EventListener\\ContentDownloadRouteReferenceListener\:\:configureOptions\(\) has no return type specified\.$#'
identifier: missingType.return
@@ -2316,12 +2292,6 @@ parameters:
count: 1
path: src/bundle/Core/Fragment/InlineFragmentRenderer.php
- -
- message: '#^Property Ibexa\\Bundle\\Core\\Fragment\\InlineFragmentRenderer\:\:\$siteAccess is never read, only written\.$#'
- identifier: property.onlyWritten
- count: 1
- path: src/bundle/Core/Fragment/InlineFragmentRenderer.php
-
-
message: '#^Trait Ibexa\\Bundle\\Core\\Fragment\\SiteAccessSerializationTrait is used zero times and is not analysed\.$#'
identifier: trait.unused
@@ -2370,24 +2340,6 @@ parameters:
count: 1
path: src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php
- -
- message: '#^Method Ibexa\\Bundle\\Core\\Imagine\\Cache\\AliasGeneratorDecorator\:\:setSiteAccess\(\) has no return type specified\.$#'
- identifier: missingType.return
- count: 1
- path: src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php
-
- -
- message: '#^Property Ibexa\\Bundle\\Core\\Imagine\\Cache\\AliasGeneratorDecorator\:\:\$siteAccess \(Ibexa\\Core\\MVC\\Symfony\\SiteAccess\) does not accept Ibexa\\Core\\MVC\\Symfony\\SiteAccess\|null\.$#'
- identifier: assign.propertyType
- count: 1
- path: src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php
-
- -
- message: '#^Property Ibexa\\Core\\MVC\\Symfony\\SiteAccess\:\:\$name \(string\) on left side of \?\? is not nullable\.$#'
- identifier: nullCoalesce.property
- count: 2
- path: src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php
-
-
message: '#^Method Ibexa\\Bundle\\Core\\Imagine\\Cache\\Resolver\\RelativeResolver\:\:rewriteUrl\(\) has parameter \$url with no type specified\.$#'
identifier: missingType.parameter
@@ -2736,12 +2688,6 @@ parameters:
count: 1
path: src/bundle/Core/Matcher/ServiceAwareMatcherFactory.php
- -
- message: '#^Cannot access property \$name on Ibexa\\Core\\MVC\\Symfony\\SiteAccess\|null\.$#'
- identifier: property.nonObject
- count: 2
- path: src/bundle/Core/SiteAccess/Config/ComplexConfigProcessor.php
-
-
message: '#^Method Ibexa\\Bundle\\Core\\SiteAccess\\Matcher\:\:setMatchingConfiguration\(\) has no return type specified\.$#'
identifier: missingType.return
@@ -6636,18 +6582,6 @@ parameters:
count: 1
path: src/lib/Helper/ContentPreviewHelper.php
- -
- message: '#^Method Ibexa\\Core\\Helper\\ContentPreviewHelper\:\:setSiteAccess\(\) has no return type specified\.$#'
- identifier: missingType.return
- count: 1
- path: src/lib/Helper/ContentPreviewHelper.php
-
- -
- message: '#^Property Ibexa\\Core\\Helper\\ContentPreviewHelper\:\:\$originalSiteAccess \(Ibexa\\Core\\MVC\\Symfony\\SiteAccess\) does not accept Ibexa\\Core\\MVC\\Symfony\\SiteAccess\|null\.$#'
- identifier: assign.propertyType
- count: 1
- path: src/lib/Helper/ContentPreviewHelper.php
-
-
message: '#^Cannot access property \$fieldTypeIdentifier on Ibexa\\Contracts\\Core\\Repository\\Values\\ContentType\\FieldDefinition\|null\.$#'
identifier: property.nonObject
diff --git a/phpunit.baseline.xml b/phpunit.baseline.xml
index a5f3364ad8..0b1d89c96e 100644
--- a/phpunit.baseline.xml
+++ b/phpunit.baseline.xml
@@ -38,6 +38,8 @@
+
+
diff --git a/src/bundle/Core/Command/DebugConfigResolverCommand.php b/src/bundle/Core/Command/DebugConfigResolverCommand.php
index 8062d34248..8d7ee62cb4 100644
--- a/src/bundle/Core/Command/DebugConfigResolverCommand.php
+++ b/src/bundle/Core/Command/DebugConfigResolverCommand.php
@@ -8,7 +8,7 @@
namespace Ibexa\Bundle\Core\Command;
use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
-use Ibexa\Core\MVC\Symfony\SiteAccess;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
@@ -28,15 +28,14 @@ class DebugConfigResolverCommand extends Command
/** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */
private $configResolver;
- /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */
- private $siteAccess;
+ private SiteAccessServiceInterface $siteAccessService;
public function __construct(
ConfigResolverInterface $configResolver,
- SiteAccess $siteAccess
+ SiteAccessServiceInterface $siteAccessService
) {
$this->configResolver = $configResolver;
- $this->siteAccess = $siteAccess;
+ $this->siteAccessService = $siteAccessService;
parent::__construct();
}
@@ -102,7 +101,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return self::SUCCESS;
}
- $output->writeln('SiteAccess name: ' . $this->siteAccess->name);
+ $siteAccess = $this->siteAccessService->getCurrent();
+ $output->writeln('SiteAccess name: ' . ($siteAccess !== null ? $siteAccess->name : 'none'));
$output->writeln('Parameter:');
$cloner = new VarCloner();
diff --git a/src/bundle/Core/DependencyInjection/Compiler/FragmentPass.php b/src/bundle/Core/DependencyInjection/Compiler/FragmentPass.php
index 2ddea81531..e7ec9ae847 100644
--- a/src/bundle/Core/DependencyInjection/Compiler/FragmentPass.php
+++ b/src/bundle/Core/DependencyInjection/Compiler/FragmentPass.php
@@ -11,6 +11,7 @@
use Ibexa\Bundle\Core\Fragment\FragmentListenerFactory;
use Ibexa\Bundle\Core\Fragment\InlineFragmentRenderer;
use Ibexa\Bundle\Core\Fragment\SiteAccessSerializerInterface;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -49,7 +50,11 @@ public function process(ContainerBuilder $container): void
$container->setDefinition($renamedId, $definition);
$decoratedDef = new ChildDefinition(DecoratedFragmentRenderer::class);
- $decoratedDef->setArguments([new Reference($renamedId), new Reference(SiteAccessSerializerInterface::class)]);
+ $decoratedDef->setArguments([
+ new Reference($renamedId),
+ new Reference(SiteAccessSerializerInterface::class),
+ new Reference(SiteAccessServiceInterface::class),
+ ]);
$decoratedDef->setPublic($public);
$decoratedDef->setTags($tags);
// Special treatment for inline fragment renderer, to fit ESI renderer constructor type hinting (forced to InlineFragmentRenderer)
diff --git a/src/bundle/Core/DependencyInjection/Compiler/SecurityPass.php b/src/bundle/Core/DependencyInjection/Compiler/SecurityPass.php
index 43e5d14dc5..7c9a2b3e4b 100644
--- a/src/bundle/Core/DependencyInjection/Compiler/SecurityPass.php
+++ b/src/bundle/Core/DependencyInjection/Compiler/SecurityPass.php
@@ -12,7 +12,7 @@
use Ibexa\Core\MVC\Symfony\Security\Authentication\AnonymousUserAccessListener;
use Ibexa\Core\MVC\Symfony\Security\Authentication\DefaultAuthenticationSuccessHandler;
use Ibexa\Core\MVC\Symfony\Security\HttpUtils;
-use Ibexa\Core\MVC\Symfony\SiteAccess;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException;
@@ -39,10 +39,7 @@ private function registerHttpUtils(ContainerBuilder $container): void
$httpUtilsDef = $container->findDefinition('security.http_utils');
$httpUtilsDef->setClass(HttpUtils::class);
- $httpUtilsDef->addMethodCall(
- 'setSiteAccess',
- [new Reference(SiteAccess::class)]
- );
+ $httpUtilsDef->setArgument('$siteAccessService', new Reference(SiteAccessServiceInterface::class));
}
private function registerSecurityAuthenticationSuccessHandler(ContainerBuilder $container): void
diff --git a/src/bundle/Core/EventListener/ConsoleCommandListener.php b/src/bundle/Core/EventListener/ConsoleCommandListener.php
index ab882de5c5..fd5af47a96 100644
--- a/src/bundle/Core/EventListener/ConsoleCommandListener.php
+++ b/src/bundle/Core/EventListener/ConsoleCommandListener.php
@@ -8,16 +8,13 @@
namespace Ibexa\Bundle\Core\EventListener;
use Ibexa\Core\MVC\Exception\InvalidSiteAccessException;
-use Ibexa\Core\MVC\Symfony\Event\ScopeChangeEvent;
-use Ibexa\Core\MVC\Symfony\MVCEvents;
use Ibexa\Core\MVC\Symfony\SiteAccess;
-use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-class ConsoleCommandListener implements EventSubscriberInterface, SiteAccessAware
+class ConsoleCommandListener implements EventSubscriberInterface
{
/** @var string */
private $defaultSiteAccessName;
@@ -25,11 +22,7 @@ class ConsoleCommandListener implements EventSubscriberInterface, SiteAccessAwar
/** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface */
private $siteAccessProvider;
- /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */
- private $eventDispatcher;
-
- /** @var \Ibexa\Core\MVC\Symfony\SiteAccess|null */
- private $siteAccess;
+ private SiteAccessServiceInterface $siteAccessService;
/** @var bool */
private $debug;
@@ -37,12 +30,12 @@ class ConsoleCommandListener implements EventSubscriberInterface, SiteAccessAwar
public function __construct(
string $defaultSiteAccessName,
SiteAccess\SiteAccessProviderInterface $siteAccessProvider,
- EventDispatcherInterface $eventDispatcher,
+ SiteAccessServiceInterface $siteAccessService,
bool $debug = false
) {
$this->defaultSiteAccessName = $defaultSiteAccessName;
$this->siteAccessProvider = $siteAccessProvider;
- $this->eventDispatcher = $eventDispatcher;
+ $this->siteAccessService = $siteAccessService;
$this->debug = $debug;
}
@@ -57,24 +50,19 @@ public static function getSubscribedEvents(): array
public function onConsoleCommand(ConsoleCommandEvent $event)
{
- $this->siteAccess->name = $event->getInput()->getParameterOption('--siteaccess', $this->defaultSiteAccessName);
- $this->siteAccess->matchingType = 'cli';
+ $siteAccessName = $event->getInput()->getParameterOption('--siteaccess', $this->defaultSiteAccessName);
+ $siteAccess = new SiteAccess($siteAccessName, 'cli');
- if (!$this->siteAccessProvider->isDefined($this->siteAccess->name)) {
+ if (!$this->siteAccessProvider->isDefined($siteAccess->name)) {
throw new InvalidSiteAccessException(
- $this->siteAccess->name,
+ $siteAccess->name,
$this->siteAccessProvider,
- $this->siteAccess->matchingType,
+ $siteAccess->matchingType,
$this->debug
);
}
- $this->eventDispatcher->dispatch(new ScopeChangeEvent($this->siteAccess), MVCEvents::CONFIG_SCOPE_CHANGE);
- }
-
- public function setSiteAccess(?SiteAccess $siteAccess = null)
- {
- $this->siteAccess = $siteAccess;
+ $this->siteAccessService->changeSiteAccess($siteAccess);
}
public function setDebug($debug = false)
diff --git a/src/bundle/Core/Fragment/DecoratedFragmentRenderer.php b/src/bundle/Core/Fragment/DecoratedFragmentRenderer.php
index b13bc5d6c3..2f0e68d37e 100644
--- a/src/bundle/Core/Fragment/DecoratedFragmentRenderer.php
+++ b/src/bundle/Core/Fragment/DecoratedFragmentRenderer.php
@@ -8,32 +8,29 @@
namespace Ibexa\Bundle\Core\Fragment;
use Ibexa\Core\MVC\Symfony\SiteAccess;
-use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface;
use Symfony\Component\HttpKernel\Fragment\RoutableFragmentRenderer;
-class DecoratedFragmentRenderer implements FragmentRendererInterface, SiteAccessAware
+class DecoratedFragmentRenderer implements FragmentRendererInterface
{
private FragmentRendererInterface $innerRenderer;
- private ?SiteAccess $siteAccess = null;
-
private SiteAccessSerializerInterface $siteAccessSerializer;
+ private SiteAccessServiceInterface $siteAccessService;
+
public function __construct(
FragmentRendererInterface $innerRenderer,
- SiteAccessSerializerInterface $siteAccessSerializer
+ SiteAccessSerializerInterface $siteAccessSerializer,
+ SiteAccessServiceInterface $siteAccessService
) {
$this->innerRenderer = $innerRenderer;
$this->siteAccessSerializer = $siteAccessSerializer;
- }
-
- public function setSiteAccess(?SiteAccess $siteAccess = null): void
- {
- $this->siteAccess = $siteAccess;
+ $this->siteAccessService = $siteAccessService;
}
public function setFragmentPath(string $path): void
@@ -42,7 +39,7 @@ public function setFragmentPath(string $path): void
return;
}
- $matcher = $this->siteAccess?->matcher;
+ $matcher = $this->siteAccessService->getCurrent()?->matcher;
if ($matcher instanceof SiteAccess\URILexer) {
$path = $matcher->analyseLink($path);
}
diff --git a/src/bundle/Core/Fragment/InlineFragmentRenderer.php b/src/bundle/Core/Fragment/InlineFragmentRenderer.php
index cb7f348f2f..3607a53984 100644
--- a/src/bundle/Core/Fragment/InlineFragmentRenderer.php
+++ b/src/bundle/Core/Fragment/InlineFragmentRenderer.php
@@ -7,8 +7,6 @@
namespace Ibexa\Bundle\Core\Fragment;
-use Ibexa\Core\MVC\Symfony\SiteAccess;
-use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
@@ -16,12 +14,10 @@
use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer as BaseRenderer;
use Symfony\Component\HttpKernel\Fragment\RoutableFragmentRenderer;
-class InlineFragmentRenderer extends BaseRenderer implements SiteAccessAware, FragmentRendererInterface
+class InlineFragmentRenderer extends BaseRenderer implements FragmentRendererInterface
{
protected FragmentRendererInterface $innerRenderer;
- private ?SiteAccess $siteAccess = null;
-
private SiteAccessSerializerInterface $siteAccessSerializer;
public function __construct(
@@ -39,11 +35,6 @@ public function setFragmentPath($path): void
}
}
- public function setSiteAccess(?SiteAccess $siteAccess = null): void
- {
- $this->siteAccess = $siteAccess;
- }
-
/**
* @param array $options
*/
diff --git a/src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php b/src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php
index 3416d2068f..03ec2447b6 100644
--- a/src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php
+++ b/src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php
@@ -11,8 +11,7 @@
use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo;
use Ibexa\Contracts\Core\Variation\Values\Variation;
use Ibexa\Contracts\Core\Variation\VariationHandler;
-use Ibexa\Core\MVC\Symfony\SiteAccess;
-use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface;
use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface;
use Symfony\Component\Routing\RequestContext;
@@ -20,7 +19,7 @@
/**
* Persistence Cache layer for AliasGenerator.
*/
-class AliasGeneratorDecorator implements VariationHandler, SiteAccessAware
+class AliasGeneratorDecorator implements VariationHandler
{
private const IMAGE_VARIATION_IDENTIFIER = 'image_variation';
private const IMAGE_VARIATION_SITEACCESS_IDENTIFIER = 'image_variation_siteaccess';
@@ -36,15 +35,14 @@ class AliasGeneratorDecorator implements VariationHandler, SiteAccessAware
/** @var \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface */
private $cache;
- /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */
- private $siteAccess;
-
/** @var \Symfony\Component\Routing\RequestContext */
private $requestContext;
/** @var \Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface */
private $cacheIdentifierGenerator;
+ private SiteAccessServiceInterface $siteAccessService;
+
/**
* @param \Ibexa\Contracts\Core\Variation\VariationHandler $aliasGenerator
* @param \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface $cache
@@ -55,12 +53,14 @@ public function __construct(
VariationHandler $aliasGenerator,
TagAwareAdapterInterface $cache,
RequestContext $requestContext,
- CacheIdentifierGeneratorInterface $cacheIdentifierGenerator
+ CacheIdentifierGeneratorInterface $cacheIdentifierGenerator,
+ SiteAccessServiceInterface $siteAccessService
) {
$this->aliasGenerator = $aliasGenerator;
$this->cache = $cache;
$this->requestContext = $requestContext;
$this->cacheIdentifierGenerator = $cacheIdentifierGenerator;
+ $this->siteAccessService = $siteAccessService;
}
/**
@@ -87,14 +87,6 @@ public function getVariation(Field $field, VersionInfo $versionInfo, string $var
return $image;
}
- /**
- * @param \Ibexa\Core\MVC\Symfony\SiteAccess|null $siteAccess
- */
- public function setSiteAccess(?SiteAccess $siteAccess = null)
- {
- $this->siteAccess = $siteAccess;
- }
-
/**
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field
* @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo
@@ -104,9 +96,11 @@ public function setSiteAccess(?SiteAccess $siteAccess = null)
*/
private function getCacheKey(Field $field, VersionInfo $versionInfo, $variationName): string
{
+ $siteAccess = $this->siteAccessService->getCurrent();
+
return sprintf(
$this->cacheIdentifierGenerator->generateKey(self::IMAGE_VARIATION_IDENTIFIER, [], true) . '-%s-%s-%s-%d-%d-%d-%s-%s',
- $this->siteAccess->name ?? 'default',
+ $siteAccess !== null ? $siteAccess->name : 'default',
$this->requestContext->getScheme(),
$this->requestContext->getHost(),
$this->requestContext->getScheme() === 'https' ? $this->requestContext->getHttpsPort() : $this->requestContext->getHttpPort(),
@@ -120,11 +114,12 @@ private function getCacheKey(Field $field, VersionInfo $versionInfo, $variationN
private function getTagsForVariation(Field $field, VersionInfo $versionInfo, string $variationName): array
{
$contentId = $versionInfo->getContentInfo()->id;
+ $siteAccess = $this->siteAccessService->getCurrent();
return [
$this->cacheIdentifierGenerator->generateTag(self::IMAGE_VARIATION_IDENTIFIER),
$this->cacheIdentifierGenerator->generateTag(self::IMAGE_VARIATION_NAME_IDENTIFIER, [$variationName]),
- $this->cacheIdentifierGenerator->generateTag(self::IMAGE_VARIATION_SITEACCESS_IDENTIFIER, [$this->siteAccess->name ?? 'default']),
+ $this->cacheIdentifierGenerator->generateTag(self::IMAGE_VARIATION_SITEACCESS_IDENTIFIER, [$siteAccess !== null ? $siteAccess->name : 'default']),
$this->cacheIdentifierGenerator->generateTag(self::IMAGE_VARIATION_CONTENT_IDENTIFIER, [$contentId]),
$this->cacheIdentifierGenerator->generateTag(self::IMAGE_VARIATION_FIELD_IDENTIFIER, [$field->id]),
$this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$contentId]),
diff --git a/src/bundle/Core/Resources/config/commands.yml b/src/bundle/Core/Resources/config/commands.yml
index f8d089acc5..87ebdf2176 100644
--- a/src/bundle/Core/Resources/config/commands.yml
+++ b/src/bundle/Core/Resources/config/commands.yml
@@ -12,7 +12,7 @@ services:
class: Ibexa\Bundle\Core\Command\DebugConfigResolverCommand
arguments:
- '@ibexa.config.resolver'
- - '@Ibexa\Core\MVC\Symfony\SiteAccess'
+ - '@Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface'
tags:
- { name: console.command }
diff --git a/src/bundle/Core/Resources/config/helpers.yml b/src/bundle/Core/Resources/config/helpers.yml
index ceb16c89cf..9e154f1e8d 100644
--- a/src/bundle/Core/Resources/config/helpers.yml
+++ b/src/bundle/Core/Resources/config/helpers.yml
@@ -16,9 +16,9 @@ services:
arguments: ['@Ibexa\Core\Helper\TranslationHelper', '@ibexa.api.service.field_type']
Ibexa\Core\Helper\ContentPreviewHelper:
- arguments: ["@event_dispatcher", '@Ibexa\Core\MVC\Symfony\SiteAccess\Router']
- calls:
- - [setSiteAccess, ['@Ibexa\Core\MVC\Symfony\SiteAccess']]
+ arguments:
+ - '@Ibexa\Core\MVC\Symfony\SiteAccess\Router'
+ - '@Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface'
Ibexa\Bundle\Core\EventListener\ConfigScopeListener:
arguments:
@@ -50,7 +50,7 @@ services:
Ibexa\Bundle\Core\SiteAccess\Config\ComplexConfigProcessor:
arguments:
$configResolver: '@ibexa.config.resolver'
- $siteAccessService: '@Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService'
+ $siteAccessService: '@Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface'
Ibexa\Contracts\Core\SiteAccess\ConfigProcessor:
alias: Ibexa\Bundle\Core\SiteAccess\Config\ComplexConfigProcessor
diff --git a/src/bundle/Core/Resources/config/image.yml b/src/bundle/Core/Resources/config/image.yml
index d3a1f4fe91..c26d10e462 100644
--- a/src/bundle/Core/Resources/config/image.yml
+++ b/src/bundle/Core/Resources/config/image.yml
@@ -68,8 +68,7 @@ services:
- '@ibexa.cache_pool'
- '@router.request_context'
- '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface'
- calls:
- - [setSiteAccess, ['@Ibexa\Core\MVC\Symfony\SiteAccess']]
+ - '@Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface'
ibexa.image_alias.imagine.alias_generator:
class: Ibexa\Bundle\Core\Imagine\AliasGenerator
diff --git a/src/bundle/Core/Resources/config/routing.yml b/src/bundle/Core/Resources/config/routing.yml
index 1e0fbe170d..0e04eff1ee 100644
--- a/src/bundle/Core/Resources/config/routing.yml
+++ b/src/bundle/Core/Resources/config/routing.yml
@@ -20,8 +20,7 @@ services:
$siteAccessRouter: '@Ibexa\Core\MVC\Symfony\SiteAccess\Router'
$nonSiteAccessAwareRoutes: '%ibexa.default_router.non_site_access_aware_routes%'
$logger: '@?logger'
- calls:
- - [setSiteAccess, ['@?Ibexa\Core\MVC\Symfony\SiteAccess']]
+ $siteAccessService: '@Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface'
tags:
- { name: monolog.logger, channel: router }
@@ -37,6 +36,7 @@ services:
$siteAccessRouter: '@Ibexa\Core\MVC\Symfony\SiteAccess\Router'
$eventDispatcher: '@event_dispatcher'
$serializer: '@ibexa.core.mvc.serializer'
+ $siteAccessService: '@Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface'
tags:
- { name: kernel.event_subscriber }
@@ -45,6 +45,7 @@ services:
arguments:
$requestStack: '@request_stack'
$eventDispatcher: '@event_dispatcher'
+ $siteAccessService: '@Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface'
tags:
- { name: kernel.event_subscriber }
@@ -53,7 +54,6 @@ services:
abstract: true
calls:
- [setRequestContext, ["@router.request_context"]]
- - [setSiteAccess, ['@?Ibexa\Core\MVC\Symfony\SiteAccess']]
- [setSiteAccessRouter, ['@Ibexa\Core\MVC\Symfony\SiteAccess\Router']]
- [setLogger, ["@?logger"]]
@@ -77,6 +77,7 @@ services:
- '@ibexa.api.repository'
- "@router.default"
- '@ibexa.config.resolver'
+ - '@Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface'
- '%ibexa.urlalias_generator.charmap%'
parent: Ibexa\Core\MVC\Symfony\Routing\Generator
@@ -110,8 +111,7 @@ services:
arguments:
- '@ibexa.siteaccess.provider'
- '@ibexa.config.resolver'
- calls:
- - [setSiteAccess, ['@Ibexa\Core\MVC\Symfony\SiteAccess']]
+ - '@event_dispatcher'
tags:
- { name: kernel.event_subscriber }
diff --git a/src/bundle/Core/Resources/config/services.yml b/src/bundle/Core/Resources/config/services.yml
index 8501e54cbc..59ecbcc452 100644
--- a/src/bundle/Core/Resources/config/services.yml
+++ b/src/bundle/Core/Resources/config/services.yml
@@ -74,10 +74,8 @@ services:
arguments:
- '%ibexa.site_access.default%'
- '@ibexa.siteaccess.provider'
- - "@event_dispatcher"
+ - '@Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface'
- "%kernel.debug%"
- calls:
- - [setSiteAccess, ['@Ibexa\Core\MVC\Symfony\SiteAccess']]
tags:
- { name: kernel.event_subscriber }
@@ -169,7 +167,6 @@ services:
arguments: []
calls:
- [setFragmentPath, ["%fragment.path%"]]
- - [setSiteAccess, ['@Ibexa\Core\MVC\Symfony\SiteAccess']]
abstract: true
Ibexa\Bundle\Core\Fragment\DirectFragmentRenderer:
diff --git a/src/bundle/Core/Routing/DefaultRouter.php b/src/bundle/Core/Routing/DefaultRouter.php
index da3000e2e3..e99fd8055f 100644
--- a/src/bundle/Core/Routing/DefaultRouter.php
+++ b/src/bundle/Core/Routing/DefaultRouter.php
@@ -11,8 +11,8 @@
use Ibexa\Core\MVC\Symfony\Routing\RequestContextFactory;
use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest;
use Ibexa\Core\MVC\Symfony\SiteAccess;
-use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware;
use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Ibexa\Core\MVC\Symfony\SiteAccess\URILexer;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -28,10 +28,8 @@
* Matching honours the `semanticPathinfo` request attribute set by the SiteAccess matcher, and link generation
* prepends the SiteAccess URI part (for URI-based SiteAccess matchers) and supports the `siteaccess` route parameter.
*/
-final class DefaultRouter implements RouterInterface, RequestMatcherInterface, WarmableInterface, SiteAccessAware
+final class DefaultRouter implements RouterInterface, RequestMatcherInterface, WarmableInterface
{
- private ?SiteAccess $siteAccess = null;
-
/**
* @param string[] $nonSiteAccessAwareRoutes route name prefixes that are not supposed to be SiteAccess aware,
* i.e. routes pointing to asset generation
@@ -39,16 +37,12 @@ final class DefaultRouter implements RouterInterface, RequestMatcherInterface, W
public function __construct(
private readonly RouterInterface&RequestMatcherInterface $innerRouter,
private readonly SiteAccessRouterInterface $siteAccessRouter,
+ private readonly SiteAccessServiceInterface $siteAccessService,
private readonly array $nonSiteAccessAwareRoutes = [],
private readonly ?LoggerInterface $logger = null
) {
}
- public function setSiteAccess(?SiteAccess $siteAccess = null): void
- {
- $this->siteAccess = $siteAccess;
- }
-
public function setContext(RequestContext $context): void
{
$this->innerRouter->setContext($context);
@@ -93,7 +87,7 @@ public function matchRequest(Request $request): array
*/
public function generate(string $name, array $parameters = [], int $referenceType = self::ABSOLUTE_PATH): string
{
- $siteAccess = $this->siteAccess;
+ $siteAccess = $this->siteAccessService->getCurrent();
$originalContext = $context = $this->getContext();
$isSiteAccessAware = $this->isSiteAccessAwareRoute($name);
@@ -105,7 +99,7 @@ public function generate(string $name, array $parameters = [], int $referenceTyp
$context = $this->getContextBySimplifiedRequest($siteAccess->matcher->getRequest());
$this->setContext($context);
} else {
- $siteAccess = $this->siteAccess;
+ $siteAccess = $this->siteAccessService->getCurrent();
$this->logger?->notice("Could not generate a link using provided 'siteaccess' parameter: {$parameters['siteaccess']}. Generating using current context.");
}
diff --git a/src/bundle/Core/SiteAccess/Config/ComplexConfigProcessor.php b/src/bundle/Core/SiteAccess/Config/ComplexConfigProcessor.php
index 5329e4dffc..8ea2c66d60 100644
--- a/src/bundle/Core/SiteAccess/Config/ComplexConfigProcessor.php
+++ b/src/bundle/Core/SiteAccess/Config/ComplexConfigProcessor.php
@@ -11,8 +11,9 @@
use Ibexa\Bundle\Core\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParser;
use Ibexa\Contracts\Core\SiteAccess\ConfigProcessor;
use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
+use Ibexa\Core\Base\Exceptions\InvalidArgumentException;
use Ibexa\Core\MVC\Exception\ParameterNotFoundException;
-use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use function str_replace;
final class ComplexConfigProcessor implements ConfigProcessor
@@ -22,7 +23,7 @@ final class ComplexConfigProcessor implements ConfigProcessor
/** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */
private $configResolver;
- /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService */
+ /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface */
private $siteAccessService;
/** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParserInterface */
@@ -30,7 +31,7 @@ final class ComplexConfigProcessor implements ConfigProcessor
public function __construct(
ConfigResolverInterface $configResolver,
- SiteAccessService $siteAccessService
+ SiteAccessServiceInterface $siteAccessService
) {
$this->configResolver = $configResolver;
$this->siteAccessService = $siteAccessService;
@@ -41,7 +42,7 @@ public function __construct(
public function processComplexSetting(string $setting): string
{
- $siteAccessName = $this->siteAccessService->getCurrent()->name;
+ $siteAccessName = $this->getCurrentSiteAccessName();
if (!$this->configResolver->hasParameter($setting, null, $siteAccessName)) {
throw new ParameterNotFoundException($setting, null, [$siteAccessName]);
@@ -74,7 +75,7 @@ public function processSettingValue(string $value): string
$dynamicSettingValue = $this->configResolver->getParameter(
$parts['param'],
$parts['namespace'],
- $parts['scope'] ?? $this->siteAccessService->getCurrent()->name
+ $parts['scope'] ?? $this->getCurrentSiteAccessName()
);
$value = str_replace($dynamicSetting, $dynamicSettingValue, $value);
@@ -82,4 +83,14 @@ public function processSettingValue(string $value): string
return $value;
}
+
+ private function getCurrentSiteAccessName(): string
+ {
+ $siteAccess = $this->siteAccessService->getCurrent();
+ if ($siteAccess === null) {
+ throw new InvalidArgumentException('siteAccess', 'no SiteAccess given and none currently set');
+ }
+
+ return $siteAccess->name;
+ }
}
diff --git a/src/lib/Helper/ContentPreviewHelper.php b/src/lib/Helper/ContentPreviewHelper.php
index ed375dd2a7..d57f458ccc 100644
--- a/src/lib/Helper/ContentPreviewHelper.php
+++ b/src/lib/Helper/ContentPreviewHelper.php
@@ -9,23 +9,16 @@
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
-use Ibexa\Core\MVC\Symfony\Event\ScopeChangeEvent;
-use Ibexa\Core\MVC\Symfony\MVCEvents;
use Ibexa\Core\MVC\Symfony\SiteAccess;
-use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware;
use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
-class ContentPreviewHelper implements SiteAccessAware
+class ContentPreviewHelper
{
- /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */
- protected $eventDispatcher;
-
/** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface */
protected $siteAccessRouter;
- /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */
- protected $originalSiteAccess;
+ private SiteAccessServiceInterface $siteAccessService;
/** @var bool */
private $previewActive = false;
@@ -36,25 +29,18 @@ class ContentPreviewHelper implements SiteAccessAware
/** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location */
private $previewedLocation;
- public function __construct(EventDispatcherInterface $eventDispatcher, SiteAccessRouterInterface $siteAccessRouter)
+ public function __construct(SiteAccessRouterInterface $siteAccessRouter, SiteAccessServiceInterface $siteAccessService)
{
- $this->eventDispatcher = $eventDispatcher;
$this->siteAccessRouter = $siteAccessRouter;
- }
-
- public function setSiteAccess(?SiteAccess $siteAccess = null)
- {
- $this->originalSiteAccess = $siteAccess;
+ $this->siteAccessService = $siteAccessService;
}
/**
* Return original SiteAccess.
- *
- * @return \Ibexa\Core\MVC\Symfony\SiteAccess
*/
- public function getOriginalSiteAccess()
+ public function getOriginalSiteAccess(): ?SiteAccess
{
- return $this->originalSiteAccess;
+ return $this->siteAccessService->getCurrent();
}
/**
@@ -66,23 +52,15 @@ public function getOriginalSiteAccess()
*/
public function changeConfigScope($siteAccessName)
{
- $event = new ScopeChangeEvent($this->siteAccessRouter->matchByName($siteAccessName));
- $this->eventDispatcher->dispatch($event, MVCEvents::CONFIG_SCOPE_CHANGE);
-
- return $event->getSiteAccess();
+ return $this->siteAccessService->changeSiteAccess($this->siteAccessRouter->matchByName($siteAccessName));
}
/**
* Restores original config scope.
- *
- * @return \Ibexa\Core\MVC\Symfony\SiteAccess
*/
- public function restoreConfigScope()
+ public function restoreConfigScope(): ?SiteAccess
{
- $event = new ScopeChangeEvent($this->originalSiteAccess);
- $this->eventDispatcher->dispatch($event, MVCEvents::CONFIG_SCOPE_RESTORE);
-
- return $event->getSiteAccess();
+ return $this->siteAccessService->restoreSiteAccess();
}
/**
@@ -99,7 +77,6 @@ public function isPreviewActive()
public function setPreviewActive($previewActive)
{
$this->previewActive = (bool)$previewActive;
- $this->originalSiteAccess = clone $this->originalSiteAccess;
}
/**
diff --git a/src/lib/MVC/Symfony/Controller/Content/PreviewController.php b/src/lib/MVC/Symfony/Controller/Content/PreviewController.php
index 5ce1da3e2a..98e8009073 100644
--- a/src/lib/MVC/Symfony/Controller/Content/PreviewController.php
+++ b/src/lib/MVC/Symfony/Controller/Content/PreviewController.php
@@ -16,6 +16,7 @@
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
use Ibexa\Core\Base\Exceptions\BadStateException;
+use Ibexa\Core\Base\Exceptions\InvalidArgumentException;
use Ibexa\Core\Helper\ContentPreviewHelper;
use Ibexa\Core\Helper\PreviewLocationProvider;
use Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator;
@@ -120,40 +121,46 @@ public function previewContentAction(
throw new AccessDeniedException();
}
- $siteAccess = $this->previewHelper->getOriginalSiteAccess();
- // Only switch if $siteAccessName is set and different from original
- if ($siteAccessName !== null && $siteAccessName !== $siteAccess->name) {
- $siteAccess = $this->previewHelper->changeConfigScope($siteAccessName);
- }
-
try {
- $viewType = $request->query->get('viewType', ViewManagerInterface::VIEW_TYPE_FULL);
- $response = $this->kernel->handle(
- $this->getForwardRequest($location, $content, $siteAccess, $request, $language, $viewType),
- HttpKernelInterface::SUB_REQUEST,
- false
- );
- } catch (APINotFoundException $e) {
- $message = sprintf('Location (%s) not found or not available in requested language (%s)', $location->id, $language);
- $this->logger->warning(
- sprintf('%s %s', $message, 'when loading the preview page'),
- ['exception' => $e]
- );
- if ($this->debugMode) {
- throw new BadStateException('Preview page', $message, $e);
+ $siteAccess = $this->previewHelper->getOriginalSiteAccess();
+ if ($siteAccess === null) {
+ throw new InvalidArgumentException('siteAccess', 'no SiteAccess given and none currently set');
}
- return new Response($message);
- } catch (Exception $e) {
- return $this->buildResponseForGenericPreviewError($location, $content, $e);
- }
- $response->headers->addCacheControlDirective('no-cache', true);
- $response->setPrivate();
+ // Only switch if $siteAccessName is set and different from original
+ if ($siteAccessName !== null && $siteAccessName !== $siteAccess->name) {
+ $siteAccess = $this->previewHelper->changeConfigScope($siteAccessName);
+ }
- $this->previewHelper->restoreConfigScope();
- $this->previewHelper->setPreviewActive(false);
+ try {
+ $viewType = $request->query->get('viewType', ViewManagerInterface::VIEW_TYPE_FULL);
+ $response = $this->kernel->handle(
+ $this->getForwardRequest($location, $content, $siteAccess, $request, $language, $viewType),
+ HttpKernelInterface::SUB_REQUEST,
+ false
+ );
+ } catch (APINotFoundException $e) {
+ $message = sprintf('Location (%s) not found or not available in requested language (%s)', $location->id, $language);
+ $this->logger->warning(
+ sprintf('%s %s', $message, 'when loading the preview page'),
+ ['exception' => $e]
+ );
+ if ($this->debugMode) {
+ throw new BadStateException('Preview page', $message, $e);
+ }
+
+ return new Response($message);
+ } catch (Exception $e) {
+ return $this->buildResponseForGenericPreviewError($location, $content, $e);
+ }
+ $response->headers->addCacheControlDirective('no-cache', true);
+ $response->setPrivate();
- return $response;
+ return $response;
+ } finally {
+ $this->previewHelper->restoreConfigScope();
+ $this->previewHelper->setPreviewActive(false);
+ }
}
/**
diff --git a/src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php b/src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php
index c172971cfe..2f7dacd594 100644
--- a/src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php
+++ b/src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php
@@ -12,10 +12,12 @@
use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest;
use Ibexa\Core\MVC\Symfony\SiteAccess;
use Ibexa\Core\MVC\Symfony\SiteAccess\Router as SiteAccessRouter;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\RequestEvent;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Serializer\SerializerInterface;
@@ -31,14 +33,18 @@ class SiteAccessMatchListener implements EventSubscriberInterface
private SerializerInterface $serializer;
+ private SiteAccessServiceInterface $siteAccessService;
+
public function __construct(
SiteAccessRouter $siteAccessRouter,
EventDispatcherInterface $eventDispatcher,
- SerializerInterface $serializer
+ SerializerInterface $serializer,
+ SiteAccessServiceInterface $siteAccessService
) {
$this->siteAccessRouter = $siteAccessRouter;
$this->eventDispatcher = $eventDispatcher;
$this->serializer = $serializer;
+ $this->siteAccessService = $siteAccessService;
}
public static function getSubscribedEvents(): array
@@ -89,6 +95,15 @@ public function onKernelRequest(RequestEvent $event): void
if ($siteaccess instanceof SiteAccess) {
$siteAccessEvent = new PostSiteAccessMatchEvent($siteaccess, $request, $event->getRequestType());
$this->eventDispatcher->dispatch($siteAccessEvent, MVCEvents::SITEACCESS);
+
+ // SiteAccessService establishes the base of its stack by reacting to this same event
+ // for MAIN_REQUEST (see SiteAccessService::onSiteAccessMatch()); a sub-request's
+ // SiteAccess is a nested change on top of that, so push it the same way any other
+ // caller would, via changeSiteAccess(). SiteAccessRestoreListener pops it back off via
+ // restoreSiteAccess() once this sub-request finishes.
+ if ($event->getRequestType() === HttpKernelInterface::SUB_REQUEST) {
+ $this->siteAccessService->changeSiteAccess($siteaccess);
+ }
}
}
diff --git a/src/lib/MVC/Symfony/EventListener/SiteAccessRestoreListener.php b/src/lib/MVC/Symfony/EventListener/SiteAccessRestoreListener.php
index 7f21fad7d0..c9ea80ee44 100644
--- a/src/lib/MVC/Symfony/EventListener/SiteAccessRestoreListener.php
+++ b/src/lib/MVC/Symfony/EventListener/SiteAccessRestoreListener.php
@@ -11,6 +11,7 @@
use Ibexa\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent;
use Ibexa\Core\MVC\Symfony\MVCEvents;
use Ibexa\Core\MVC\Symfony\SiteAccess;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
@@ -19,9 +20,12 @@
use Symfony\Component\HttpKernel\KernelEvents;
/**
- * Restores the parent request's SiteAccess when a sub-request finishes, by re-dispatching
- * MVCEvents::SITEACCESS — its listeners mutate shared state for every sub-request. Mirrors
- * {@see \Symfony\Component\HttpKernel\EventListener\RouterListener::onKernelFinishRequest()}.
+ * Restores the parent request's SiteAccess when a sub-request finishes:
+ * - for SiteAccessService, by calling restoreSiteAccess(), popping the entry
+ * SiteAccessMatchListener pushed via changeSiteAccess() when this sub-request started.
+ * - for not-yet-migrated listeners still reacting to MVCEvents::SITEACCESS directly (mutating
+ * shared state for every sub-request), by re-dispatching that event. Mirrors
+ * {@see \Symfony\Component\HttpKernel\EventListener\RouterListener::onKernelFinishRequest()}.
*/
final class SiteAccessRestoreListener implements EventSubscriberInterface
{
@@ -29,12 +33,16 @@ final class SiteAccessRestoreListener implements EventSubscriberInterface
private EventDispatcherInterface $eventDispatcher;
+ private SiteAccessServiceInterface $siteAccessService;
+
public function __construct(
RequestStack $requestStack,
- EventDispatcherInterface $eventDispatcher
+ EventDispatcherInterface $eventDispatcher,
+ SiteAccessServiceInterface $siteAccessService
) {
$this->requestStack = $requestStack;
$this->eventDispatcher = $eventDispatcher;
+ $this->siteAccessService = $siteAccessService;
}
public static function getSubscribedEvents(): array
@@ -50,6 +58,12 @@ public function onKernelFinishRequest(FinishRequestEvent $event): void
return;
}
+ // Mirrors the condition SiteAccessMatchListener used to decide whether to push, so the
+ // pop here only ever undoes what this exact sub-request pushed.
+ if ($event->getRequest()->attributes->get('siteaccess') instanceof SiteAccess) {
+ $this->siteAccessService->restoreSiteAccess();
+ }
+
// The finishing request is still on the stack, so this is the request control returns to
$parentRequest = $this->requestStack->getParentRequest();
if ($parentRequest === null) {
diff --git a/src/lib/MVC/Symfony/Routing/Generator.php b/src/lib/MVC/Symfony/Routing/Generator.php
index 9729d0b90a..16d8982e8a 100644
--- a/src/lib/MVC/Symfony/Routing/Generator.php
+++ b/src/lib/MVC/Symfony/Routing/Generator.php
@@ -8,8 +8,8 @@
namespace Ibexa\Core\MVC\Symfony\Routing;
use Ibexa\Core\MVC\Symfony\SiteAccess;
-use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware;
use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RequestContext;
@@ -17,16 +17,18 @@
/**
* Base class for Ibexa Url generation.
*/
-abstract class Generator implements SiteAccessAware
+abstract class Generator
{
protected RequestContext $requestContext;
protected SiteAccessRouterInterface $siteAccessRouter;
- protected ?SiteAccess $siteAccess;
-
protected ?LoggerInterface $logger;
+ public function __construct(private readonly SiteAccessServiceInterface $siteAccessService)
+ {
+ }
+
public function setRequestContext(RequestContext $requestContext): void
{
$this->requestContext = $requestContext;
@@ -37,11 +39,6 @@ public function setSiteAccessRouter(SiteAccessRouterInterface $siteAccessRouter)
$this->siteAccessRouter = $siteAccessRouter;
}
- public function setSiteAccess(?SiteAccess $siteAccess = null): void
- {
- $this->siteAccess = $siteAccess;
- }
-
public function setLogger(?LoggerInterface $logger = null): void
{
$this->logger = $logger;
@@ -57,7 +54,7 @@ public function setLogger(?LoggerInterface $logger = null): void
*/
public function generate(mixed $urlResource, array $parameters, int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string
{
- $siteAccess = $this->siteAccess;
+ $siteAccess = $this->siteAccessService->getCurrent();
$requestContext = $this->requestContext;
// Retrieving the appropriate SiteAccess to generate the link for.
@@ -69,7 +66,7 @@ public function generate(mixed $urlResource, array $parameters, int $referenceTy
$siteAccess->matcher->getRequest()
);
} elseif (isset($this->logger)) {
- $siteAccess = $this->siteAccess;
+ $siteAccess = $this->siteAccessService->getCurrent();
$this->logger->notice("Could not generate a link using provided 'siteaccess' parameter: {$parameters['siteaccess']}. Generating using current context.");
unset($parameters['siteaccess']);
}
diff --git a/src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php b/src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php
index de2c8b66f2..ae10adaa2d 100644
--- a/src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php
+++ b/src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php
@@ -11,6 +11,7 @@
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
use Ibexa\Core\MVC\Symfony\Routing\Generator;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Symfony\Component\Routing\RouterInterface;
/**
@@ -47,8 +48,15 @@ class UrlAliasGenerator extends Generator
/**
* @param array $unsafeCharMap
*/
- public function __construct(Repository $repository, RouterInterface $defaultRouter, ConfigResolverInterface $configResolver, array $unsafeCharMap = [])
- {
+ public function __construct(
+ Repository $repository,
+ RouterInterface $defaultRouter,
+ ConfigResolverInterface $configResolver,
+ SiteAccessServiceInterface $siteAccessService,
+ array $unsafeCharMap = []
+ ) {
+ parent::__construct($siteAccessService);
+
$this->repository = $repository;
$this->defaultRouter = $defaultRouter;
$this->configResolver = $configResolver;
diff --git a/src/lib/MVC/Symfony/Security/HttpUtils.php b/src/lib/MVC/Symfony/Security/HttpUtils.php
index 4e1e8c725b..9000c9674e 100644
--- a/src/lib/MVC/Symfony/Security/HttpUtils.php
+++ b/src/lib/MVC/Symfony/Security/HttpUtils.php
@@ -8,22 +8,28 @@
namespace Ibexa\Core\MVC\Symfony\Security;
use Ibexa\Core\MVC\Symfony\SiteAccess;
-use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
+use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
use Symfony\Component\Security\Http\HttpUtils as BaseHttpUtils;
-class HttpUtils extends BaseHttpUtils implements SiteAccessAware
+class HttpUtils extends BaseHttpUtils
{
- private ?SiteAccess $siteAccess;
-
- public function setSiteAccess(?SiteAccess $siteAccess = null): void
- {
- $this->siteAccess = $siteAccess;
+ public function __construct(
+ ?UrlGeneratorInterface $urlGenerator = null,
+ UrlMatcherInterface|RequestMatcherInterface|null $urlMatcher = null,
+ ?string $domainRegexp = null,
+ ?string $secureDomainRegexp = null,
+ private ?SiteAccessServiceInterface $siteAccessService = null,
+ ) {
+ parent::__construct($urlGenerator, $urlMatcher, $domainRegexp, $secureDomainRegexp);
}
private function analyzeLink(string $path): string
{
- $matcher = $this->siteAccess?->matcher;
+ $matcher = $this->siteAccessService?->getCurrent()?->matcher;
if ($path[0] === '/' && $matcher instanceof SiteAccess\URILexer) {
$path = $matcher->analyseLink($path);
}
diff --git a/src/lib/MVC/Symfony/SiteAccess/SiteAccessService.php b/src/lib/MVC/Symfony/SiteAccess/SiteAccessService.php
index 41eebdd75b..e25d2ccd7d 100644
--- a/src/lib/MVC/Symfony/SiteAccess/SiteAccessService.php
+++ b/src/lib/MVC/Symfony/SiteAccess/SiteAccessService.php
@@ -11,57 +11,101 @@
use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
use Ibexa\Core\Base\Exceptions\InvalidArgumentException;
use Ibexa\Core\Base\Exceptions\NotFoundException;
+use Ibexa\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent;
use Ibexa\Core\MVC\Symfony\Event\ScopeChangeEvent;
use Ibexa\Core\MVC\Symfony\MVCEvents;
use Ibexa\Core\MVC\Symfony\SiteAccess;
use function iterator_to_array;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
-class SiteAccessService implements SiteAccessServiceInterface, SiteAccessAware, EventSubscriberInterface
+/**
+ * changeSiteAccess()/restoreSiteAccess() are the single, supported way to swap the current
+ * SiteAccess (content preview, CLI --siteaccess, sub-requests). Manually dispatching
+ * ScopeChangeEvent under MVCEvents::CONFIG_SCOPE_CHANGE/CONFIG_SCOPE_RESTORE is deprecated and
+ * only kept as a fallback for not-yet-migrated listeners — see onConfigScopeChange().
+ */
+class SiteAccessService implements SiteAccessServiceInterface, EventSubscriberInterface
{
/** @var list<\Ibexa\Core\MVC\Symfony\SiteAccess> */
private array $siteAccessStack = [];
+ private bool $isDispatchingInternally = false;
+
public function __construct(
private readonly SiteAccessProviderInterface $provider,
- private readonly ConfigResolverInterface $configResolver
+ private readonly ConfigResolverInterface $configResolver,
+ private readonly EventDispatcherInterface $eventDispatcher
) {
}
public static function getSubscribedEvents(): array
{
return [
+ MVCEvents::SITEACCESS => 'onSiteAccessMatch',
MVCEvents::CONFIG_SCOPE_CHANGE => 'onConfigScopeChange',
MVCEvents::CONFIG_SCOPE_RESTORE => 'onConfigScopeRestore',
];
}
- public function setSiteAccess(?SiteAccess $siteAccess = null): void
+ /**
+ * Establishes the base of the SiteAccess stack for a new top-level request. Sub-requests are
+ * NOT handled here: the listener matching their SiteAccess calls changeSiteAccess() directly,
+ * same as any other caller.
+ */
+ public function onSiteAccessMatch(PostSiteAccessMatchEvent $event): void
{
- $this->siteAccessStack = $siteAccess !== null ? [$siteAccess] : [];
+ if ($event->getRequestType() !== HttpKernelInterface::MAIN_REQUEST) {
+ return;
+ }
+
+ $this->siteAccessStack = [$event->getSiteAccess()];
}
/**
- * Pushes the new SiteAccess onto the stack, so that getCurrent() reflects it until the
- * matching restore happens.
+ * @deprecated 6.0, to be removed in 7.0. Dispatching ScopeChangeEvent manually under
+ * MVCEvents::CONFIG_SCOPE_CHANGE is deprecated; call changeSiteAccess() instead,
+ * which dispatches this event for you. Kept so not-yet-migrated listeners keep
+ * working, but no longer the supported way to change the current SiteAccess.
*/
public function onConfigScopeChange(ScopeChangeEvent $event): void
{
+ if ($this->isDispatchingInternally) {
+ return;
+ }
+
+ trigger_deprecation(
+ 'ibexa/core',
+ '6.0',
+ 'Dispatching %s manually under MVCEvents::CONFIG_SCOPE_CHANGE is deprecated and will no longer be reflected by %s::getCurrent() in 7.0. Call %s::changeSiteAccess() instead.',
+ ScopeChangeEvent::class,
+ self::class,
+ SiteAccessServiceInterface::class
+ );
+
$this->siteAccessStack[] = $event->getSiteAccess();
}
/**
- * Pops the SiteAccess pushed by the matching onConfigScopeChange(), but never below one
- * remaining entry: the bottom-most entry (the current request's SiteAccess) must survive an
- * unbalanced restore.
+ * @deprecated 6.0, to be removed in 7.0. See onConfigScopeChange().
*/
public function onConfigScopeRestore(ScopeChangeEvent $event): void
{
- if (count($this->siteAccessStack) <= 1) {
+ if ($this->isDispatchingInternally) {
return;
}
- array_pop($this->siteAccessStack);
+ trigger_deprecation(
+ 'ibexa/core',
+ '6.0',
+ 'Dispatching %s manually under MVCEvents::CONFIG_SCOPE_RESTORE is deprecated and will no longer be reflected by %s::getCurrent() in 7.0. Call %s::restoreSiteAccess() instead.',
+ ScopeChangeEvent::class,
+ self::class,
+ SiteAccessServiceInterface::class
+ );
+
+ $this->popSiteAccessStack();
}
public function exists(string $name): bool
@@ -85,12 +129,40 @@ public function getAll(): iterable
public function getCurrent(): ?SiteAccess
{
- return $this->siteAccessStack !== [] ? end($this->siteAccessStack) : null;
+ if ($this->siteAccessStack === []) {
+ return null;
+ }
+
+ // Defensive: a SiteAccess with an "uninitialized" matcher (see
+ // SiteAccess::MATCHING_TYPE_UNINITIALIZED) is not a real current SiteAccess even if it
+ // somehow ended up on the stack — surface it as null rather than a misleading value.
+ $current = end($this->siteAccessStack);
+
+ return $current->matchingType !== SiteAccess::MATCHING_TYPE_UNINITIALIZED ? $current : null;
+ }
+
+ public function changeSiteAccess(SiteAccess $siteAccess): SiteAccess
+ {
+ $this->siteAccessStack[] = $siteAccess;
+ $this->dispatchScopeChangeEvent($siteAccess, MVCEvents::CONFIG_SCOPE_CHANGE);
+
+ return $siteAccess;
+ }
+
+ public function restoreSiteAccess(): ?SiteAccess
+ {
+ $this->popSiteAccessStack();
+ $siteAccess = $this->getCurrent();
+ if ($siteAccess !== null) {
+ $this->dispatchScopeChangeEvent($siteAccess, MVCEvents::CONFIG_SCOPE_RESTORE);
+ }
+
+ return $siteAccess;
}
public function getSiteAccessesRelation(?SiteAccess $siteAccess = null): array
{
- $siteAccess = $siteAccess ?? $this->getCurrent();
+ $siteAccess ??= $this->getCurrent();
if ($siteAccess === null) {
throw new InvalidArgumentException('siteAccess', 'no SiteAccess given and none currently set');
}
@@ -122,4 +194,30 @@ public function getSiteAccessesRelation(?SiteAccess $siteAccess = null): array
return $saRelationMap[$repository][$rootLocationId];
}
+
+ /**
+ * Pops the SiteAccess stack, but never below one remaining entry: the bottom-most entry
+ * (the current top-level request's SiteAccess) must survive an unbalanced restore.
+ */
+ private function popSiteAccessStack(): void
+ {
+ if (count($this->siteAccessStack) > 1) {
+ array_pop($this->siteAccessStack);
+ }
+ }
+
+ /**
+ * Dispatches $eventName with isDispatchingInternally set, so onConfigScopeChange()/
+ * onConfigScopeRestore() can tell this dispatch apart from a manually-constructed one and
+ * skip reacting to it (the stack mutation already happened above).
+ */
+ private function dispatchScopeChangeEvent(SiteAccess $siteAccess, string $eventName): void
+ {
+ $this->isDispatchingInternally = true;
+ try {
+ $this->eventDispatcher->dispatch(new ScopeChangeEvent($siteAccess), $eventName);
+ } finally {
+ $this->isDispatchingInternally = false;
+ }
+ }
}
diff --git a/src/lib/MVC/Symfony/SiteAccess/SiteAccessServiceInterface.php b/src/lib/MVC/Symfony/SiteAccess/SiteAccessServiceInterface.php
index b95cfda289..61c43278e4 100644
--- a/src/lib/MVC/Symfony/SiteAccess/SiteAccessServiceInterface.php
+++ b/src/lib/MVC/Symfony/SiteAccess/SiteAccessServiceInterface.php
@@ -29,6 +29,20 @@ public function getAll(): iterable;
public function getCurrent(): ?SiteAccess;
+ /**
+ * Switches the current SiteAccess to $siteAccess, remembering the previous one so it can be
+ * restored later via restoreSiteAccess(). Dispatches a ScopeChangeEvent under
+ * MVCEvents::CONFIG_SCOPE_CHANGE. This is the supported way to change the current SiteAccess —
+ * do not dispatch that event manually.
+ */
+ public function changeSiteAccess(SiteAccess $siteAccess): SiteAccess;
+
+ /**
+ * Restores the SiteAccess that was current before the last changeSiteAccess() call.
+ * Dispatches a ScopeChangeEvent under MVCEvents::CONFIG_SCOPE_RESTORE.
+ */
+ public function restoreSiteAccess(): ?SiteAccess;
+
/**
* Handles relation between SiteAccesses. Related SiteAccesses share the same repository and root location id.
*
diff --git a/tests/bundle/Core/DependencyInjection/Compiler/FragmentPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/FragmentPassTest.php
index 72c78331d0..6f1b82ef2d 100644
--- a/tests/bundle/Core/DependencyInjection/Compiler/FragmentPassTest.php
+++ b/tests/bundle/Core/DependencyInjection/Compiler/FragmentPassTest.php
@@ -12,6 +12,7 @@
use Ibexa\Bundle\Core\Fragment\FragmentListenerFactory;
use Ibexa\Bundle\Core\Fragment\InlineFragmentRenderer;
use Ibexa\Bundle\Core\Fragment\SiteAccessSerializerInterface;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
@@ -67,7 +68,7 @@ public function testProcess(): void
$decoratedInlineDef = $this->container->getDefinition('fragment.renderer.inline');
self::assertSame(['kernel.fragment_renderer' => [[]]], $decoratedInlineDef->getTags());
self::assertEquals(
- [new Reference('fragment.renderer.inline.inner'), new Reference(SiteAccessSerializerInterface::class)],
+ [new Reference('fragment.renderer.inline.inner'), new Reference(SiteAccessSerializerInterface::class), new Reference(SiteAccessServiceInterface::class)],
$decoratedInlineDef->getArguments()
);
self::assertSame(InlineFragmentRenderer::class, $decoratedInlineDef->getClass());
@@ -76,7 +77,7 @@ public function testProcess(): void
$decoratedEsiDef = $this->container->getDefinition('fragment.renderer.esi');
self::assertSame(['kernel.fragment_renderer' => [[]]], $decoratedEsiDef->getTags());
self::assertEquals(
- [new Reference('fragment.renderer.esi.inner'), new Reference(SiteAccessSerializerInterface::class)],
+ [new Reference('fragment.renderer.esi.inner'), new Reference(SiteAccessSerializerInterface::class), new Reference(SiteAccessServiceInterface::class)],
$decoratedEsiDef->getArguments()
);
@@ -84,7 +85,7 @@ public function testProcess(): void
$decoratedHincludeDef = $this->container->getDefinition('fragment.renderer.hinclude');
self::assertSame(['kernel.fragment_renderer' => [[]]], $decoratedHincludeDef->getTags());
self::assertEquals(
- [new Reference('fragment.renderer.hinclude.inner'), new Reference(SiteAccessSerializerInterface::class)],
+ [new Reference('fragment.renderer.hinclude.inner'), new Reference(SiteAccessSerializerInterface::class), new Reference(SiteAccessServiceInterface::class)],
$decoratedHincludeDef->getArguments()
);
}
diff --git a/tests/bundle/Core/DependencyInjection/Compiler/SecurityPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/SecurityPassTest.php
index ea08ce8da5..c4b1a87eef 100644
--- a/tests/bundle/Core/DependencyInjection/Compiler/SecurityPassTest.php
+++ b/tests/bundle/Core/DependencyInjection/Compiler/SecurityPassTest.php
@@ -13,7 +13,7 @@
use Ibexa\Contracts\Core\Repository\UserService;
use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
use Ibexa\Core\MVC\Symfony\Security\Authentication\AnonymousUserAccessListener;
-use Ibexa\Core\MVC\Symfony\SiteAccess;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
@@ -29,7 +29,7 @@ protected function setUp(): void
$this->setDefinition('security.http_utils', new Definition());
$this->setDefinition('security.authentication.success_handler', new Definition());
$this->setDefinition(ConfigResolverInterface::class, new Definition());
- $this->setDefinition(SiteAccess::class, new Definition());
+ $this->setDefinition(SiteAccessServiceInterface::class, new Definition());
$this->setDefinition(PermissionResolver::class, new Definition());
$this->setDefinition(UserService::class, new Definition());
}
@@ -43,10 +43,10 @@ public function testAlteredHttpUtils(): void
{
$this->compile();
- $this->assertContainerBuilderHasServiceDefinitionWithMethodCall(
+ $this->assertContainerBuilderHasServiceDefinitionWithArgument(
'security.http_utils',
- 'setSiteAccess',
- [new Reference(SiteAccess::class)]
+ '$siteAccessService',
+ new Reference(SiteAccessServiceInterface::class)
);
}
diff --git a/tests/bundle/Core/EventListener/ConsoleCommandListenerTest.php b/tests/bundle/Core/EventListener/ConsoleCommandListenerTest.php
index 0bd8a53484..7d1e2cbdef 100644
--- a/tests/bundle/Core/EventListener/ConsoleCommandListenerTest.php
+++ b/tests/bundle/Core/EventListener/ConsoleCommandListenerTest.php
@@ -10,6 +10,7 @@
use Ibexa\Bundle\Core\EventListener\ConsoleCommandListener;
use Ibexa\Core\MVC\Exception\InvalidSiteAccessException;
use Ibexa\Core\MVC\Symfony\SiteAccess;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Ibexa\Tests\Bundle\Core\EventListener\Stubs\TestOutput;
use PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\TestCase;
@@ -20,17 +21,13 @@
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\Output;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class ConsoleCommandListenerTest extends TestCase
{
private const INVALID_SA_NAME = 'foo';
- /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */
- private $siteAccess;
-
- /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\PHPUnit\Framework\MockObject\MockObject */
- private $dispatcher;
+ /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface|\PHPUnit\Framework\MockObject\MockObject */
+ private $siteAccessService;
/** @var \Ibexa\Bundle\Core\EventListener\ConsoleCommandListener */
private $listener;
@@ -46,11 +43,8 @@ class ConsoleCommandListenerTest extends TestCase
protected function setUp(): void
{
parent::setUp();
- $this->siteAccess = new SiteAccess('test');
- $this->dispatcher = $this->createMock(EventDispatcherInterface::class);
- $this->listener = new ConsoleCommandListener('default', $this->getSiteAccessProviderMock(), $this->dispatcher);
- $this->listener->setSiteAccess($this->siteAccess);
- $this->dispatcher->addSubscriber($this->listener);
+ $this->siteAccessService = $this->createMock(SiteAccessServiceInterface::class);
+ $this->listener = new ConsoleCommandListener('default', $this->getSiteAccessProviderMock(), $this->siteAccessService);
$this->inputDefinition = new InputDefinition([new InputOption('siteaccess', null, InputOption::VALUE_OPTIONAL)]);
$this->testOutput = new TestOutput(Output::VERBOSITY_QUIET, true);
$this->command = self::createStub(Command::class);
@@ -71,8 +65,8 @@ public function testInvalidSiteAccessDev(): void
$this->expectException(InvalidSiteAccessException::class);
$this->expectExceptionMessageMatches('/^Invalid SiteAccess \'foo\', matched by .+\\. Valid SiteAccesses are/');
- $this->dispatcher->expects(self::never())
- ->method('dispatch');
+ $this->siteAccessService->expects(self::never())
+ ->method('changeSiteAccess');
$input = new ArrayInput(['--siteaccess' => 'foo'], $this->inputDefinition);
$event = new ConsoleCommandEvent($this->command, $input, $this->testOutput);
$this->listener->setDebug(true);
@@ -84,8 +78,8 @@ public function testInvalidSiteAccessProd(): void
$this->expectException(InvalidSiteAccessException::class);
$this->expectExceptionMessageMatches('/^Invalid SiteAccess \'foo\', matched by .+\\.$/');
- $this->dispatcher->expects(self::never())
- ->method('dispatch');
+ $this->siteAccessService->expects(self::never())
+ ->method('changeSiteAccess');
$input = new ArrayInput(['--siteaccess' => 'foo'], $this->inputDefinition);
$event = new ConsoleCommandEvent($this->command, $input, $this->testOutput);
$this->listener->setDebug(false);
@@ -94,22 +88,22 @@ public function testInvalidSiteAccessProd(): void
public function testValidSiteAccess(): void
{
- $this->dispatcher->expects(self::once())
- ->method('dispatch');
+ $this->siteAccessService->expects(self::once())
+ ->method('changeSiteAccess')
+ ->with(new SiteAccess('site1', 'cli'));
$input = new ArrayInput(['--siteaccess' => 'site1'], $this->inputDefinition);
$event = new ConsoleCommandEvent($this->command, $input, $this->testOutput);
$this->listener->onConsoleCommand($event);
- self::assertEquals(new SiteAccess('site1', 'cli'), $this->siteAccess);
}
public function testDefaultSiteAccess(): void
{
- $this->dispatcher->expects(self::once())
- ->method('dispatch');
+ $this->siteAccessService->expects(self::once())
+ ->method('changeSiteAccess')
+ ->with(new SiteAccess('default', 'cli'));
$input = new ArrayInput([], $this->inputDefinition);
$event = new ConsoleCommandEvent($this->command, $input, $this->testOutput);
$this->listener->onConsoleCommand($event);
- self::assertEquals(new SiteAccess('default', 'cli'), $this->siteAccess);
}
private function getSiteAccessProviderMock(): SiteAccess\SiteAccessProviderInterface
diff --git a/tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php b/tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php
index 3867ebe41e..e63383a559 100644
--- a/tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php
+++ b/tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php
@@ -10,6 +10,7 @@
use Ibexa\Bundle\Core\Fragment\DecoratedFragmentRenderer;
use Ibexa\Bundle\Core\Fragment\SiteAccessSerializer;
use Ibexa\Core\MVC\Symfony\SiteAccess;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\MockObject\MockObject;
use Symfony\Component\HttpFoundation\Request;
@@ -24,10 +25,13 @@ class DecoratedFragmentRendererTest extends FragmentRendererBaseTestCase
{
protected FragmentRendererInterface & MockObject $innerRenderer;
+ protected SiteAccessServiceInterface & MockObject $siteAccessService;
+
protected function setUp(): void
{
parent::setUp();
$this->innerRenderer = $this->createMock(FragmentRendererInterface::class);
+ $this->siteAccessService = $this->createMock(SiteAccessServiceInterface::class);
}
public function testSetFragmentPathNotRoutableRenderer(): void
@@ -38,8 +42,9 @@ public function testSetFragmentPathNotRoutableRenderer(): void
->expects(self::never())
->method('analyseLink');
+ $this->siteAccessService->method('getCurrent')->willReturn($siteAccess);
+
$renderer = $this->getRenderer();
- $renderer->setSiteAccess($siteAccess);
if ($renderer instanceof RoutableFragmentRenderer) {
$renderer->setFragmentPath('foo');
}
@@ -55,13 +60,14 @@ public function testSetFragmentPath(): void
->with('/foo')
->willReturn('/bar/foo');
+ $this->siteAccessService->method('getCurrent')->willReturn($siteAccess);
+
$innerRenderer = $this->createMock(RoutableFragmentRenderer::class);
$innerRenderer
->expects(self::once())
->method('setFragmentPath')
->with('/bar/foo');
- $renderer = new DecoratedFragmentRenderer($innerRenderer, new SiteAccessSerializer($this->getSerializer()));
- $renderer->setSiteAccess($siteAccess);
+ $renderer = new DecoratedFragmentRenderer($innerRenderer, new SiteAccessSerializer($this->getSerializer()), $this->siteAccessService);
$renderer->setFragmentPath('/foo');
}
@@ -136,11 +142,8 @@ public function getRequest(SiteAccess $siteAccess): Request
return $request;
}
- /**
- * @return \Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface&\Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware
- */
public function getRenderer(): FragmentRendererInterface
{
- return new DecoratedFragmentRenderer($this->innerRenderer, new SiteAccessSerializer($this->getSerializer()));
+ return new DecoratedFragmentRenderer($this->innerRenderer, new SiteAccessSerializer($this->getSerializer()), $this->siteAccessService);
}
}
diff --git a/tests/bundle/Core/Routing/DefaultRouterTest.php b/tests/bundle/Core/Routing/DefaultRouterTest.php
index 27fd6e65ce..23764a69c7 100644
--- a/tests/bundle/Core/Routing/DefaultRouterTest.php
+++ b/tests/bundle/Core/Routing/DefaultRouterTest.php
@@ -12,6 +12,7 @@
use Ibexa\Bundle\Core\SiteAccess\Matcher;
use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest;
use Ibexa\Core\MVC\Symfony\SiteAccess;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\MockObject\MockObject;
@@ -34,6 +35,8 @@ final class DefaultRouterTest extends TestCase
private SiteAccess\SiteAccessRouterInterface&MockObject $siteAccessRouter;
+ private SiteAccessServiceInterface&MockObject $siteAccessService;
+
private RequestContext $requestContext;
protected function setUp(): void
@@ -41,13 +44,14 @@ protected function setUp(): void
parent::setUp();
$this->innerRouter = $this->createMock(Router::class);
$this->siteAccessRouter = $this->createMock(SiteAccess\SiteAccessRouterInterface::class);
+ $this->siteAccessService = $this->createMock(SiteAccessServiceInterface::class);
$this->requestContext = new RequestContext();
$this->innerRouter->method('getContext')->willReturnCallback(fn (): RequestContext => $this->requestContext);
}
private function createRouter(): DefaultRouter
{
- return new DefaultRouter($this->innerRouter, $this->siteAccessRouter, self::NON_SITEACCESS_AWARE_ROUTES);
+ return new DefaultRouter($this->innerRouter, $this->siteAccessRouter, $this->siteAccessService, self::NON_SITEACCESS_AWARE_ROUTES);
}
public function testMatchRequestWithSemanticPathinfo(): void
@@ -169,8 +173,8 @@ public function testGenerateWithSiteAccess(
substr($urlComponents['path'], 0, strpos($urlComponents['path'], $relevantUri))
);
+ $this->siteAccessService->method('getCurrent')->willReturn(new SiteAccess($saName, 'test', $matcher));
$router = $this->createRouter();
- $router->setSiteAccess(new SiteAccess($saName, 'test', $matcher));
self::assertSame($expectedUrl, $router->generate($routeName, [], $referenceType));
}
@@ -214,8 +218,8 @@ public function testGenerateReverseSiteAccessMatch(): void
->with($routeName, [])
->willReturn($urlGenerated);
+ $this->siteAccessService->method('getCurrent')->willReturn(new SiteAccess('test', 'test', $this->createMock(Matcher::class)));
$router = $this->createRouter();
- $router->setSiteAccess(new SiteAccess('test', 'test', $this->createMock(Matcher::class)));
self::assertSame(
$urlGenerated,
@@ -266,7 +270,7 @@ public function testWarmUpDelegatesToInnerRouter(): void
->with('/cache', '/build')
->willReturn(['/cache/routes.php']);
- $router = new DefaultRouter($innerRouter, $this->siteAccessRouter);
+ $router = new DefaultRouter($innerRouter, $this->siteAccessRouter, $this->siteAccessService);
self::assertSame(['/cache/routes.php'], $router->warmUp('/cache', '/build'));
}
diff --git a/tests/bundle/IO/DependencyInjection/IbexaIOExtensionTest.php b/tests/bundle/IO/DependencyInjection/IbexaIOExtensionTest.php
index 984e4f1d9c..a46cb7b31b 100644
--- a/tests/bundle/IO/DependencyInjection/IbexaIOExtensionTest.php
+++ b/tests/bundle/IO/DependencyInjection/IbexaIOExtensionTest.php
@@ -14,8 +14,12 @@
use Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory;
use Ibexa\Bundle\IO\DependencyInjection\IbexaIOExtension;
use Ibexa\Core\IO\UrlDecorator\AbsolutePrefix;
+use Ibexa\Core\MVC\Symfony\SiteAccess;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService;
use Ibexa\Tests\Integration\Core\Repository\Container\Compiler\SetAllServicesPublicPass;
use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractExtensionTestCase;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Yaml\Yaml;
class IbexaIOExtensionTest extends AbstractExtensionTestCase
@@ -86,6 +90,10 @@ public function testUrlPrefixConfigurationIsUsedToDecorateUrl(): void
);
$this->buildMinimalContainerForUrlPrefixTest();
+ $siteAccessService = $this->container->get(SiteAccessService::class);
+ self::assertInstanceOf(SiteAccessService::class, $siteAccessService);
+ $siteAccessService->changeSiteAccess(new SiteAccess('site'));
+
$decorator = $this->container->get(AbsolutePrefix::class);
self::assertEquals(
@@ -101,6 +109,7 @@ private function buildMinimalContainerForUrlPrefixTest(): void
$this->container->setParameter('kernel.debug', true);
$this->container->setParameter('kernel.project_dir', self::FIXTURES_DIR);
$this->container->setParameter('kernel.cache_dir', self::FIXTURES_DIR . '/cache');
+ $this->container->setDefinition('event_dispatcher', new Definition(EventDispatcher::class));
$this->container->addCompilerPass(new ChainConfigResolverPass());
$this->container->addCompilerPass(new SetAllServicesPublicPass());
diff --git a/tests/integration/Core/LegacyTestContainerBuilder.php b/tests/integration/Core/LegacyTestContainerBuilder.php
index 7d2e45f2dd..7038601452 100644
--- a/tests/integration/Core/LegacyTestContainerBuilder.php
+++ b/tests/integration/Core/LegacyTestContainerBuilder.php
@@ -13,7 +13,7 @@
use Ibexa\Contracts\Core\SiteAccess\ConfigProcessor;
use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
use Ibexa\Core\Base\Container\Compiler;
-use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Ibexa\Tests\Integration\Core\Repository\Container\Compiler\SetAllServicesPublicPass;
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\Config\FileLocator;
@@ -166,7 +166,7 @@ private function registerSiteAccessConfigProcessingServices(): void
$definition = new Definition(ComplexConfigProcessor::class);
$definition->setArgument('$configResolver', new Reference(ConfigResolverInterface::class));
- $definition->setArgument('$siteAccessService', new Reference(SiteAccessService::class));
+ $definition->setArgument('$siteAccessService', new Reference(SiteAccessServiceInterface::class));
$this->setDefinition(ConfigProcessor::class, $definition);
}
diff --git a/tests/integration/Core/Resources/settings/common.yml b/tests/integration/Core/Resources/settings/common.yml
index 5a43ba3cdb..e7c57f574c 100644
--- a/tests/integration/Core/Resources/settings/common.yml
+++ b/tests/integration/Core/Resources/settings/common.yml
@@ -106,13 +106,15 @@ services:
Ibexa\Core\MVC\Symfony\SiteAccess:
arguments: ['default', !php/const Ibexa\Core\MVC\Symfony\SiteAccess::MATCHING_TYPE_UNINITIALIZED]
+ event_dispatcher:
+ class: Symfony\Component\EventDispatcher\EventDispatcher
+
Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService:
class: Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService
arguments:
- '@ibexa.siteaccess.provider'
- '@ibexa.config.resolver'
- calls:
- - [setSiteAccess, ['@Ibexa\Core\MVC\Symfony\SiteAccess']]
+ - '@event_dispatcher'
Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface:
alias: 'Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService'
diff --git a/tests/lib/Helper/ContentPreviewHelperTest.php b/tests/lib/Helper/ContentPreviewHelperTest.php
index fd6940ab60..7c6b680608 100644
--- a/tests/lib/Helper/ContentPreviewHelperTest.php
+++ b/tests/lib/Helper/ContentPreviewHelperTest.php
@@ -10,17 +10,15 @@
use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent;
use Ibexa\Contracts\Core\Repository\Values\Content\Location as APILocation;
use Ibexa\Core\Helper\ContentPreviewHelper;
-use Ibexa\Core\MVC\Symfony\Event\ScopeChangeEvent;
-use Ibexa\Core\MVC\Symfony\MVCEvents;
use Ibexa\Core\MVC\Symfony\SiteAccess;
use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use PHPUnit\Framework\TestCase;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class ContentPreviewHelperTest extends TestCase
{
/** @var \PHPUnit\Framework\MockObject\MockObject */
- private $eventDispatcher;
+ private $siteAccessService;
/** @var \PHPUnit\Framework\MockObject\MockObject */
private $siteAccessRouter;
@@ -31,9 +29,9 @@ class ContentPreviewHelperTest extends TestCase
protected function setUp(): void
{
parent::setUp();
- $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
+ $this->siteAccessService = $this->createMock(SiteAccessServiceInterface::class);
$this->siteAccessRouter = $this->createMock(SiteAccessRouterInterface::class);
- $this->previewHelper = new ContentPreviewHelper($this->eventDispatcher, $this->siteAccessRouter);
+ $this->previewHelper = new ContentPreviewHelper($this->siteAccessRouter, $this->siteAccessService);
}
public function testChangeConfigScope(): void
@@ -47,14 +45,12 @@ public function testChangeConfigScope(): void
->with(self::equalTo($newSiteAccessName))
->willReturn($newSiteAccess);
- $event = new ScopeChangeEvent($newSiteAccess);
- $this->eventDispatcher
+ $this->siteAccessService
->expects(self::once())
- ->method('dispatch')
- ->with(self::equalTo($event), MVCEvents::CONFIG_SCOPE_CHANGE);
+ ->method('changeSiteAccess')
+ ->with($newSiteAccess)
+ ->willReturn($newSiteAccess);
- $originalSiteAccess = new SiteAccess('foo', 'bar');
- $this->previewHelper->setSiteAccess($originalSiteAccess);
self::assertEquals(
$newSiteAccess,
$this->previewHelper->changeConfigScope($newSiteAccessName)
@@ -64,31 +60,34 @@ public function testChangeConfigScope(): void
public function testRestoreConfigScope(): void
{
$originalSiteAccess = new SiteAccess('foo', 'bar');
- $event = new ScopeChangeEvent($originalSiteAccess);
- $this->eventDispatcher
+ $this->siteAccessService
->expects(self::once())
- ->method('dispatch')
- ->with(self::equalTo($event), MVCEvents::CONFIG_SCOPE_RESTORE);
+ ->method('restoreSiteAccess')
+ ->willReturn($originalSiteAccess);
- $this->previewHelper->setSiteAccess($originalSiteAccess);
self::assertEquals(
$originalSiteAccess,
$this->previewHelper->restoreConfigScope()
);
}
- public function testPreviewActive(): void
+ public function testGetOriginalSiteAccess(): void
{
$originalSiteAccess = new SiteAccess('foo', 'bar');
- $this->previewHelper->setSiteAccess($originalSiteAccess);
+ $this->siteAccessService
+ ->method('getCurrent')
+ ->willReturn($originalSiteAccess);
+
+ self::assertSame($originalSiteAccess, $this->previewHelper->getOriginalSiteAccess());
+ }
+ public function testPreviewActive(): void
+ {
self::assertFalse($this->previewHelper->isPreviewActive());
$this->previewHelper->setPreviewActive(true);
self::assertTrue($this->previewHelper->isPreviewActive());
$this->previewHelper->setPreviewActive(false);
self::assertFalse($this->previewHelper->isPreviewActive());
-
- self::assertNotSame($originalSiteAccess, $this->previewHelper->getOriginalSiteAccess());
}
public function testPreviewedContent(): void
diff --git a/tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php b/tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php
index 8b07fade12..59e680e18c 100644
--- a/tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php
+++ b/tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php
@@ -25,6 +25,7 @@
use Ibexa\Core\MVC\Symfony\SiteAccess;
use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher;
use Ibexa\Core\MVC\Symfony\SiteAccess\Router;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Ibexa\Core\MVC\Symfony\SiteAccessGroup;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\MockObject\MockObject;
@@ -51,6 +52,8 @@ final class SiteAccessMatchListenerTest extends TestCase
private SiteAccessMatcherRegistryInterface&MockObject $registry;
+ private SiteAccessServiceInterface&MockObject $siteAccessService;
+
private SiteAccessMatchListener $listener;
protected function setUp(): void
@@ -59,10 +62,12 @@ protected function setUp(): void
$this->saRouter = $this->createMock(Router::class);
$this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
$this->registry = $this->createMock(SiteAccessMatcherRegistryInterface::class);
+ $this->siteAccessService = $this->createMock(SiteAccessServiceInterface::class);
$this->listener = new SiteAccessMatchListener(
$this->saRouter,
$this->eventDispatcher,
- $this->getSerializer()
+ $this->getSerializer(),
+ $this->siteAccessService
);
}
@@ -229,6 +234,21 @@ public function testOnKernelRequestUserHashWithOriginalRequest(): void
$this->assertRequestHasSiteAccess($request, $originalRequest, $siteAccess);
}
+ /**
+ * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
+ * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException
+ */
+ public function testOnKernelRequestSubRequestPushesToSiteAccessService(): void
+ {
+ $siteAccess = new SiteAccess('test');
+ $scheme = 'https';
+ $host = 'phoenix-rises.fm';
+ $port = 1234;
+ $path = '/foo/bar';
+ $request = Request::create(sprintf('%s://%s:%d%s', $scheme, $host, $port, $path));
+ $this->assertRequestHasSiteAccess($request, null, $siteAccess, HttpKernelInterface::SUB_REQUEST);
+ }
+
private function serializeSiteAccess(SiteAccess $siteAccess): string
{
return $this->getSerializer()->serialize($siteAccess, 'json');
@@ -241,13 +261,14 @@ private function serializeSiteAccess(SiteAccess $siteAccess): string
private function assertRequestHasSiteAccess(
Request $request,
?Request $originalRequest,
- SiteAccess $siteAccess
+ SiteAccess $siteAccess,
+ int $requestType = HttpKernelInterface::MAIN_REQUEST
): void {
$originalRequest ??= $request;
$event = new RequestEvent(
self::createStub(HttpKernelInterface::class),
$request,
- HttpKernelInterface::MAIN_REQUEST
+ $requestType
);
$simplifiedRequest = new SimplifiedRequest(
@@ -274,6 +295,17 @@ private function assertRequestHasSiteAccess(
->with(self::equalTo($postSAMatchEvent), MVCEvents::SITEACCESS)
;
+ if ($requestType === HttpKernelInterface::SUB_REQUEST) {
+ $this->siteAccessService
+ ->expects(self::once())
+ ->method('changeSiteAccess')
+ ->with($siteAccess);
+ } else {
+ $this->siteAccessService
+ ->expects(self::never())
+ ->method('changeSiteAccess');
+ }
+
$this->listener->onKernelRequest($event);
self::assertSame($siteAccess, $request->attributes->get('siteaccess'));
}
@@ -300,6 +332,10 @@ private function dispatchRequestEvent(Request $request, SiteAccess $siteAccess):
->method('dispatch')
->with($postSAMatchEvent, MVCEvents::SITEACCESS);
+ $this->siteAccessService
+ ->expects(self::never())
+ ->method('changeSiteAccess');
+
$this->listener->onKernelRequest($event);
}
diff --git a/tests/lib/MVC/Symfony/EventListener/SiteAccessRestoreListenerTest.php b/tests/lib/MVC/Symfony/EventListener/SiteAccessRestoreListenerTest.php
index cb36739c25..7b69a30a9f 100644
--- a/tests/lib/MVC/Symfony/EventListener/SiteAccessRestoreListenerTest.php
+++ b/tests/lib/MVC/Symfony/EventListener/SiteAccessRestoreListenerTest.php
@@ -12,6 +12,7 @@
use Ibexa\Core\MVC\Symfony\EventListener\SiteAccessRestoreListener;
use Ibexa\Core\MVC\Symfony\MVCEvents;
use Ibexa\Core\MVC\Symfony\SiteAccess;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\MockObject\MockObject;
@@ -33,6 +34,8 @@ final class SiteAccessRestoreListenerTest extends TestCase
private RequestStack $requestStack;
+ private SiteAccessServiceInterface&MockObject $siteAccessService;
+
private SiteAccessRestoreListener $listener;
protected function setUp(): void
@@ -41,9 +44,11 @@ protected function setUp(): void
$this->kernel = self::createStub(HttpKernelInterface::class);
$this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
$this->requestStack = new RequestStack();
+ $this->siteAccessService = $this->createMock(SiteAccessServiceInterface::class);
$this->listener = new SiteAccessRestoreListener(
$this->requestStack,
- $this->eventDispatcher
+ $this->eventDispatcher,
+ $this->siteAccessService
);
}
@@ -78,6 +83,10 @@ static function (PostSiteAccessMatchEvent $event) use ($parentSiteAccess, $paren
MVCEvents::SITEACCESS
);
+ $this->siteAccessService
+ ->expects(self::once())
+ ->method('restoreSiteAccess');
+
$this->listener->onKernelFinishRequest(
new FinishRequestEvent($this->kernel, $subRequest, HttpKernelInterface::SUB_REQUEST)
);
@@ -97,12 +106,26 @@ public function testNoDispatch(array $requests, int $requestType): void
self::assertNotNull($finishingRequest);
$this->eventDispatcher->expects(self::never())->method('dispatch');
+ $this->siteAccessService->expects(self::never())->method('restoreSiteAccess');
$this->listener->onKernelFinishRequest(
new FinishRequestEvent($this->kernel, $finishingRequest, $requestType)
);
}
+ public function testRestoresSiteAccessServiceWithoutDispatchingWhenNoParentRequest(): void
+ {
+ $subRequest = self::createRequestWithSiteAccess(new SiteAccess('admin', 'uri:element'));
+ $this->requestStack->push($subRequest);
+
+ $this->eventDispatcher->expects(self::never())->method('dispatch');
+ $this->siteAccessService->expects(self::once())->method('restoreSiteAccess');
+
+ $this->listener->onKernelFinishRequest(
+ new FinishRequestEvent($this->kernel, $subRequest, HttpKernelInterface::SUB_REQUEST)
+ );
+ }
+
/**
* @return iterable
*/
diff --git a/tests/lib/MVC/Symfony/Routing/GeneratorTest.php b/tests/lib/MVC/Symfony/Routing/GeneratorTest.php
index f2b355648b..2e2798b0cd 100644
--- a/tests/lib/MVC/Symfony/Routing/GeneratorTest.php
+++ b/tests/lib/MVC/Symfony/Routing/GeneratorTest.php
@@ -10,6 +10,7 @@
use Ibexa\Core\MVC\Symfony\Routing\Generator;
use Ibexa\Core\MVC\Symfony\SiteAccess;
use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Ibexa\Core\MVC\Symfony\SiteAccess\URILexer;
use Ibexa\Core\Repository\Values\Content\Location;
use PHPUnit\Framework\Attributes\DataProvider;
@@ -29,12 +30,19 @@ class GeneratorTest extends TestCase
/** @var \PHPUnit\Framework\MockObject\MockObject */
private $logger;
+ /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface|\PHPUnit\Framework\MockObject\MockObject */
+ private $siteAccessService;
+
protected function setUp(): void
{
parent::setUp();
$this->siteAccessRouter = $this->createMock(SiteAccessRouterInterface::class);
$this->logger = $this->createMock(LoggerInterface::class);
- $this->generator = $this->getMockForAbstractClass(Generator::class);
+ $this->siteAccessService = $this->createMock(SiteAccessServiceInterface::class);
+ $this->generator = $this->getMockBuilder(Generator::class)
+ ->setConstructorArgs([$this->siteAccessService])
+ ->onlyMethods(['doGenerate'])
+ ->getMock();
$this->generator->setSiteAccessRouter($this->siteAccessRouter);
$this->generator->setLogger($this->logger);
}
@@ -61,7 +69,7 @@ public static function generateProvider(): array
public function testSimpleGenerate($urlResource, array $parameters, $referenceType): void
{
$matcher = $this->createMock(URILexer::class);
- $this->generator->setSiteAccess(new SiteAccess('test', 'fake', $matcher));
+ $this->siteAccessService->method('getCurrent')->willReturn(new SiteAccess('test', 'fake', $matcher));
$baseUrl = '/base/url';
$requestContext = new RequestContext($baseUrl);
@@ -72,14 +80,14 @@ public function testSimpleGenerate($urlResource, array $parameters, $referenceTy
->expects(self::once())
->method('doGenerate')
->with($urlResource, $parameters)
- ->will(self::returnValue($uri));
+ ->willReturn($uri);
$fullUri = $baseUrl . $uri;
$matcher
->expects(self::once())
->method('analyseLink')
->with($uri)
- ->will(self::returnValue($uri));
+ ->willReturn($uri);
if ($referenceType === UrlGeneratorInterface::ABSOLUTE_URL) {
$fullUri = $requestContext->getScheme() . '://' . $requestContext->getHost() . $baseUrl . $uri;
@@ -92,7 +100,7 @@ public function testSimpleGenerate($urlResource, array $parameters, $referenceTy
public function testGenerateWithSiteAccessNoReverseMatch($urlResource, array $parameters, $referenceType): void
{
$matcher = $this->createMock(URILexer::class);
- $this->generator->setSiteAccess(new SiteAccess('test', 'test', $matcher));
+ $this->siteAccessService->method('getCurrent')->willReturn(new SiteAccess('test', 'test', $matcher));
$baseUrl = '/base/url';
$requestContext = new RequestContext($baseUrl);
@@ -103,14 +111,14 @@ public function testGenerateWithSiteAccessNoReverseMatch($urlResource, array $pa
->expects(self::once())
->method('doGenerate')
->with($urlResource, $parameters)
- ->will(self::returnValue($uri));
+ ->willReturn($uri);
$fullUri = $baseUrl . $uri;
$matcher
->expects(self::once())
->method('analyseLink')
->with($uri)
- ->will(self::returnValue($uri));
+ ->willReturn($uri);
if ($referenceType === UrlGeneratorInterface::ABSOLUTE_URL) {
$fullUri = $requestContext->getScheme() . '://' . $requestContext->getHost() . $baseUrl . $uri;
@@ -121,7 +129,7 @@ public function testGenerateWithSiteAccessNoReverseMatch($urlResource, array $pa
->expects(self::once())
->method('matchByName')
->with($siteAccessName)
- ->will(self::returnValue(null));
+ ->willReturn(null);
$this->logger
->expects(self::once())
->method('notice');
diff --git a/tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php b/tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php
index 813ca4f24b..7e99a2426c 100644
--- a/tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php
+++ b/tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php
@@ -16,6 +16,7 @@
use Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator;
use Ibexa\Core\MVC\Symfony\SiteAccess;
use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Ibexa\Core\Repository\Mapper\RoleDomainMapper;
use Ibexa\Core\Repository\Permission\LimitationService;
use Ibexa\Core\Repository\Permission\PermissionResolver;
@@ -52,6 +53,9 @@ class UrlAliasGeneratorTest extends TestCase
/** @var \PHPUnit\Framework\MockObject\MockObject */
private $configResolver;
+ /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface|\PHPUnit\Framework\MockObject\MockObject */
+ private $siteAccessService;
+
protected function setUp(): void
{
parent::setUp();
@@ -59,6 +63,7 @@ protected function setUp(): void
$this->logger = $this->createMock(LoggerInterface::class);
$this->siteAccessRouter = self::createStub(SiteAccessRouterInterface::class);
$this->configResolver = $this->createMock(ConfigResolverInterface::class);
+ $this->siteAccessService = $this->createMock(SiteAccessServiceInterface::class);
$repositoryClass = Repository::class;
$this->repository = $repository = $this
->getMockBuilder($repositoryClass)
@@ -95,6 +100,7 @@ protected function setUp(): void
$this->repository,
$this->router,
$this->configResolver,
+ $this->siteAccessService,
$urlAliasCharmap
);
$this->urlAliasGenerator->setLogger($this->logger);
@@ -176,7 +182,7 @@ public function testDoGenerate(URLAlias $urlAlias, array $parameters, $expected)
->with($location, false)
->will(self::returnValue([$urlAlias]));
- $this->urlAliasGenerator->setSiteAccess(new SiteAccess('test', 'fake', self::createStub(SiteAccess\URILexer::class)));
+ $this->siteAccessService->method('getCurrent')->willReturn(new SiteAccess('test', 'fake', self::createStub(SiteAccess\URILexer::class)));
self::assertSame($expected, $this->urlAliasGenerator->doGenerate($location, $parameters));
}
@@ -276,7 +282,7 @@ static function ($location) use ($treeRootUrlAlias): URLAlias {
)
);
- $this->urlAliasGenerator->setSiteAccess(new SiteAccess('test', 'fake', self::createStub(SiteAccess\URILexer::class)));
+ $this->siteAccessService->method('getCurrent')->willReturn(new SiteAccess('test', 'fake', self::createStub(SiteAccess\URILexer::class)));
self::assertSame($expected, $this->urlAliasGenerator->doGenerate($location, $parameters));
}
@@ -390,7 +396,7 @@ public function testDoGenerateWithSiteAccessLoadsLocationWithLanguages(): void
->with($location, null, false, ['ger-DE'])
->willReturn($treeRootUrlAliases[$location->id]);
- $this->urlAliasGenerator->setSiteAccess(
+ $this->siteAccessService->method('getCurrent')->willReturn(
new SiteAccess(
$gerSiteAccess,
'default',
diff --git a/tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php b/tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php
index 6c48dbf3af..2e0bd7de37 100644
--- a/tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php
+++ b/tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php
@@ -18,6 +18,7 @@
use Ibexa\Core\MVC\Symfony\Routing\UrlAliasRouter;
use Ibexa\Core\MVC\Symfony\SiteAccess;
use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Ibexa\Core\MVC\Symfony\View\Manager as ViewManager;
use Ibexa\Core\Repository\Repository;
use Ibexa\Core\Repository\Values\Content\Location;
@@ -78,6 +79,7 @@ protected function setUp(): void
$repository,
self::createStub(RouterInterface::class),
self::createStub(ConfigResolverInterface::class),
+ self::createStub(SiteAccessServiceInterface::class),
]
)
->getMock();
diff --git a/tests/lib/MVC/Symfony/Security/HttpUtilsTest.php b/tests/lib/MVC/Symfony/Security/HttpUtilsTest.php
index 6dc92ec9cc..575183e575 100644
--- a/tests/lib/MVC/Symfony/Security/HttpUtilsTest.php
+++ b/tests/lib/MVC/Symfony/Security/HttpUtilsTest.php
@@ -9,6 +9,7 @@
use Ibexa\Core\MVC\Symfony\Security\HttpUtils;
use Ibexa\Core\MVC\Symfony\SiteAccess;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
@@ -20,8 +21,8 @@ class HttpUtilsTest extends TestCase
public function testGenerateUriStandard($uri, $isUriRouteName, $expected): void
{
$urlGenerator = $this->createMock(UrlGeneratorInterface::class);
- $httpUtils = new HttpUtils($urlGenerator);
- $httpUtils->setSiteAccess(new SiteAccess('test'));
+ $siteAccessService = $this->createSiteAccessServiceReturning(new SiteAccess('test'));
+ $httpUtils = new HttpUtils($urlGenerator, null, null, null, $siteAccessService);
$request = Request::create('http://ezpublish.dev/');
$request->attributes->set('siteaccess', new SiteAccess('test'));
$requestAttributes = ['foo' => 'bar', 'some' => 'thing'];
@@ -32,7 +33,7 @@ public function testGenerateUriStandard($uri, $isUriRouteName, $expected): void
->expects(self::once())
->method('generate')
->with($uri, $requestAttributes, UrlGeneratorInterface::ABSOLUTE_URL)
- ->will(self::returnValue($expected . '?' . http_build_query($requestAttributes)));
+ ->willReturn($expected . '?' . http_build_query($requestAttributes));
}
self::assertSame($expected, $httpUtils->generateUri($request, $uri));
@@ -62,13 +63,13 @@ public function testGenerateUri($uri, $isUriRouteName, $siteAccessUri, $expected
->expects(self::once())
->method('analyseLink')
->with($uri)
- ->will(self::returnValue($siteAccessUri . $uri));
+ ->willReturn($siteAccessUri . $uri);
$siteAccess->matcher = $matcher;
}
$urlGenerator = $this->createMock(UrlGeneratorInterface::class);
- $httpUtils = new HttpUtils($urlGenerator);
- $httpUtils->setSiteAccess($siteAccess);
+ $siteAccessService = $this->createSiteAccessServiceReturning($siteAccess);
+ $httpUtils = new HttpUtils($urlGenerator, null, null, null, $siteAccessService);
$request = Request::create('http://ezpublish.dev/');
$request->attributes->set('siteaccess', $siteAccess);
$requestAttributes = ['foo' => 'bar', 'some' => 'thing'];
@@ -79,7 +80,7 @@ public function testGenerateUri($uri, $isUriRouteName, $siteAccessUri, $expected
->expects(self::once())
->method('generate')
->with($uri, $requestAttributes, UrlGeneratorInterface::ABSOLUTE_URL)
- ->will(self::returnValue($expected . '?' . http_build_query($requestAttributes)));
+ ->willReturn($expected . '?' . http_build_query($requestAttributes));
}
$res = $httpUtils->generateUri($request, $uri);
@@ -102,8 +103,8 @@ public static function generateUriProvider(): array
public function testCheckRequestPathStandard(): void
{
- $httpUtils = new HttpUtils();
- $httpUtils->setSiteAccess(new SiteAccess('test'));
+ $siteAccessService = $this->createSiteAccessServiceReturning(new SiteAccess('test'));
+ $httpUtils = new HttpUtils(null, null, null, null, $siteAccessService);
$request = Request::create('http://ezpublish.dev/foo/bar');
self::assertTrue($httpUtils->checkRequestPath($request, '/foo/bar'));
}
@@ -118,12 +119,12 @@ public function testCheckRequestPath($path, $siteAccessUri, $requestUri, $expect
->expects(self::once())
->method('analyseLink')
->with($path)
- ->will(self::returnValue($siteAccessUri . $path));
+ ->willReturn($siteAccessUri . $path);
$siteAccess->matcher = $matcher;
}
- $httpUtils = new HttpUtils();
- $httpUtils->setSiteAccess($siteAccess);
+ $siteAccessService = $this->createSiteAccessServiceReturning($siteAccess);
+ $httpUtils = new HttpUtils(null, null, null, null, $siteAccessService);
$request = Request::create($requestUri);
self::assertSame($expected, $httpUtils->checkRequestPath($request, $path));
}
@@ -142,4 +143,12 @@ public static function checkRequestPathProvider(): array
['/foo/bar', '/blabla', 'http://ezpublish.dev/blabla/foo/bar', true],
];
}
+
+ private function createSiteAccessServiceReturning(SiteAccess $siteAccess): SiteAccessServiceInterface
+ {
+ $siteAccessService = $this->createMock(SiteAccessServiceInterface::class);
+ $siteAccessService->method('getCurrent')->willReturn($siteAccess);
+
+ return $siteAccessService;
+ }
}
diff --git a/tests/lib/MVC/Symfony/SiteAccess/SiteAccessServiceTest.php b/tests/lib/MVC/Symfony/SiteAccess/SiteAccessServiceTest.php
index 58056d3e4c..83458a7d73 100644
--- a/tests/lib/MVC/Symfony/SiteAccess/SiteAccessServiceTest.php
+++ b/tests/lib/MVC/Symfony/SiteAccess/SiteAccessServiceTest.php
@@ -11,13 +11,19 @@
use ArrayIterator;
use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
+use Ibexa\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent;
use Ibexa\Core\MVC\Symfony\Event\ScopeChangeEvent;
use Ibexa\Core\MVC\Symfony\MVCEvents;
use Ibexa\Core\MVC\Symfony\SiteAccess;
use Ibexa\Core\MVC\Symfony\SiteAccess\Provider\StaticSiteAccessProvider;
use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface;
use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService;
+use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
class SiteAccessServiceTest extends TestCase
{
@@ -25,10 +31,10 @@ class SiteAccessServiceTest extends TestCase
private const UNDEFINED_SA_NAME = 'undefined_sa';
private const SA_GROUP = 'group';
- /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface|\PHPUnit\Framework\MockObject\MockObject */
+ /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface&\PHPUnit\Framework\MockObject\MockObject */
private $provider;
- /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */
+ /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface&\PHPUnit\Framework\MockObject\MockObject */
private $configResolver;
/** @var \Ibexa\Core\MVC\Symfony\SiteAccess */
@@ -50,27 +56,39 @@ protected function setUp(): void
$this->configResolverParameters = $this->getConfigResolverParameters();
}
- public function testGetCurrentSiteAccess(): void
+ public function testGetCurrentIsNullInitially(): void
{
- $service = new SiteAccessService(
- self::createStub(SiteAccessProviderInterface::class),
- self::createStub(ConfigResolverInterface::class)
- );
+ self::assertNull($this->createService()->getCurrent());
+ }
- self::assertNull($service->getCurrent());
+ public function testOnSiteAccessMatchResetsStackOnMainRequest(): void
+ {
+ $service = $this->createService();
$siteAccess = new SiteAccess('default');
- $service->setSiteAccess($siteAccess);
+ $service->onSiteAccessMatch($this->createSiteAccessMatchEvent($siteAccess, HttpKernelInterface::MAIN_REQUEST));
+
self::assertSame($siteAccess, $service->getCurrent());
+ }
+
+ public function testOnSiteAccessMatchIsNoopOnSubRequest(): void
+ {
+ $service = $this->createService();
- $service->setSiteAccess(null);
- self::assertNull($service->getCurrent());
+ $mainSiteAccess = new SiteAccess('main');
+ $service->onSiteAccessMatch($this->createSiteAccessMatchEvent($mainSiteAccess, HttpKernelInterface::MAIN_REQUEST));
+
+ $subRequestSiteAccess = new SiteAccess('sub_request');
+ $service->onSiteAccessMatch($this->createSiteAccessMatchEvent($subRequestSiteAccess, HttpKernelInterface::SUB_REQUEST));
+
+ self::assertSame($mainSiteAccess, $service->getCurrent());
}
public function testGetSubscribedEvents(): void
{
self::assertSame(
[
+ MVCEvents::SITEACCESS => 'onSiteAccessMatch',
MVCEvents::CONFIG_SCOPE_CHANGE => 'onConfigScopeChange',
MVCEvents::CONFIG_SCOPE_RESTORE => 'onConfigScopeRestore',
],
@@ -78,77 +96,119 @@ public function testGetSubscribedEvents(): void
);
}
- public function testOnConfigScopeChangeMakesGetCurrentReflectTheNewSiteAccess(): void
+ public function testChangeSiteAccessPushesAndReturnsTheGivenSiteAccess(): void
{
- $service = new SiteAccessService(
- self::createStub(SiteAccessProviderInterface::class),
- self::createStub(ConfigResolverInterface::class)
- );
+ $service = $this->createService();
$baseSiteAccess = new SiteAccess('base');
- $service->setSiteAccess($baseSiteAccess);
+ $service->onSiteAccessMatch($this->createSiteAccessMatchEvent($baseSiteAccess, HttpKernelInterface::MAIN_REQUEST));
$previewSiteAccess = new SiteAccess('preview');
- $service->onConfigScopeChange(new ScopeChangeEvent($previewSiteAccess));
+ self::assertSame($previewSiteAccess, $service->changeSiteAccess($previewSiteAccess));
+ self::assertSame($previewSiteAccess, $service->getCurrent());
+ }
+ public function testChangeSiteAccessDispatchesEventWithoutTriggeringTheDeprecatedHandler(): void
+ {
+ $eventDispatcher = new EventDispatcher();
+ $service = $this->createService($eventDispatcher);
+
+ $dispatchedEvents = [];
+ $eventDispatcher->addListener(MVCEvents::CONFIG_SCOPE_CHANGE, static function (ScopeChangeEvent $event) use (&$dispatchedEvents): void {
+ $dispatchedEvents[] = $event->getSiteAccess();
+ });
+
+ $previewSiteAccess = new SiteAccess('preview');
+ $service->changeSiteAccess($previewSiteAccess);
+
+ // The event is still dispatched for other, unrelated listeners to observe...
+ self::assertSame([$previewSiteAccess], $dispatchedEvents);
+ // ...but the stack was mutated exactly once by changeSiteAccess() itself, proving
+ // the service's own onConfigScopeChange() (which would push a 2nd time) did not react.
self::assertSame($previewSiteAccess, $service->getCurrent());
}
- public function testOnConfigScopeRestoreBringsBackThePreviousSiteAccess(): void
+ public function testRestoreSiteAccessPopsStackAndReturnsThePreviousSiteAccess(): void
{
- $service = new SiteAccessService(
- self::createStub(SiteAccessProviderInterface::class),
- self::createStub(ConfigResolverInterface::class)
- );
+ $service = $this->createService();
$baseSiteAccess = new SiteAccess('base');
- $service->setSiteAccess($baseSiteAccess);
+ $service->onSiteAccessMatch($this->createSiteAccessMatchEvent($baseSiteAccess, HttpKernelInterface::MAIN_REQUEST));
$previewSiteAccess = new SiteAccess('preview');
- $service->onConfigScopeChange(new ScopeChangeEvent($previewSiteAccess));
- $service->onConfigScopeRestore(new ScopeChangeEvent($baseSiteAccess));
+ $service->changeSiteAccess($previewSiteAccess);
+ self::assertSame($baseSiteAccess, $service->restoreSiteAccess());
self::assertSame($baseSiteAccess, $service->getCurrent());
}
- public function testOnConfigScopeRestoreNeverDropsTheBaseSiteAccess(): void
+ public function testRestoreSiteAccessNeverDropsBelowTheBaseSiteAccess(): void
{
- $service = new SiteAccessService(
- self::createStub(SiteAccessProviderInterface::class),
- self::createStub(ConfigResolverInterface::class)
- );
+ $service = $this->createService();
$baseSiteAccess = new SiteAccess('base');
- $service->setSiteAccess($baseSiteAccess);
-
- $service->onConfigScopeRestore(new ScopeChangeEvent($baseSiteAccess));
+ $service->onSiteAccessMatch($this->createSiteAccessMatchEvent($baseSiteAccess, HttpKernelInterface::MAIN_REQUEST));
+ self::assertSame($baseSiteAccess, $service->restoreSiteAccess());
self::assertSame($baseSiteAccess, $service->getCurrent());
}
- public function testNestedConfigScopeChangesAndRestoresRoundTripLikeAStack(): void
+ public function testRestoreSiteAccessReturnsNullWhenStackIsEmpty(): void
{
- $service = new SiteAccessService(
- self::createStub(SiteAccessProviderInterface::class),
- self::createStub(ConfigResolverInterface::class)
- );
+ self::assertNull($this->createService()->restoreSiteAccess());
+ }
+
+ public function testNestedChangeSiteAccessAndRestoreSiteAccessRoundTripLikeAStack(): void
+ {
+ $service = $this->createService();
$baseSiteAccess = new SiteAccess('base');
- $service->setSiteAccess($baseSiteAccess);
+ $service->onSiteAccessMatch($this->createSiteAccessMatchEvent($baseSiteAccess, HttpKernelInterface::MAIN_REQUEST));
$firstSiteAccess = new SiteAccess('first');
$secondSiteAccess = new SiteAccess('second');
- $service->onConfigScopeChange(new ScopeChangeEvent($firstSiteAccess));
+ $service->changeSiteAccess($firstSiteAccess);
self::assertSame($firstSiteAccess, $service->getCurrent());
- $service->onConfigScopeChange(new ScopeChangeEvent($secondSiteAccess));
+ $service->changeSiteAccess($secondSiteAccess);
self::assertSame($secondSiteAccess, $service->getCurrent());
- $service->onConfigScopeRestore(new ScopeChangeEvent($firstSiteAccess));
- self::assertSame($firstSiteAccess, $service->getCurrent());
+ self::assertSame($firstSiteAccess, $service->restoreSiteAccess());
+ self::assertSame($baseSiteAccess, $service->restoreSiteAccess());
+ }
+
+ public function testOnConfigScopeChangeReactsToAManuallyDispatchedEventAndTriggersDeprecation(): void
+ {
+ $this->expectUserDeprecationMessage('Since ibexa/core 6.0: Dispatching ' . ScopeChangeEvent::class . ' manually under MVCEvents::CONFIG_SCOPE_CHANGE is deprecated and will no longer be reflected by ' . SiteAccessService::class . '::getCurrent() in 7.0. Call ' . SiteAccessServiceInterface::class . '::changeSiteAccess() instead.');
+
+ $eventDispatcher = new EventDispatcher();
+ $service = $this->createService($eventDispatcher);
+
+ $baseSiteAccess = new SiteAccess('base');
+ $service->onSiteAccessMatch($this->createSiteAccessMatchEvent($baseSiteAccess, HttpKernelInterface::MAIN_REQUEST));
+
+ $previewSiteAccess = new SiteAccess('preview');
+ $eventDispatcher->dispatch(new ScopeChangeEvent($previewSiteAccess), MVCEvents::CONFIG_SCOPE_CHANGE);
+
+ self::assertSame($previewSiteAccess, $service->getCurrent());
+ }
+
+ public function testOnConfigScopeRestoreReactsToAManuallyDispatchedEventAndTriggersDeprecation(): void
+ {
+ $this->expectUserDeprecationMessage('Since ibexa/core 6.0: Dispatching ' . ScopeChangeEvent::class . ' manually under MVCEvents::CONFIG_SCOPE_RESTORE is deprecated and will no longer be reflected by ' . SiteAccessService::class . '::getCurrent() in 7.0. Call ' . SiteAccessServiceInterface::class . '::restoreSiteAccess() instead.');
+
+ $eventDispatcher = new EventDispatcher();
+ $service = $this->createService($eventDispatcher);
+
+ $baseSiteAccess = new SiteAccess('base');
+ $service->onSiteAccessMatch($this->createSiteAccessMatchEvent($baseSiteAccess, HttpKernelInterface::MAIN_REQUEST));
+
+ $previewSiteAccess = new SiteAccess('preview');
+ $service->changeSiteAccess($previewSiteAccess);
+
+ $eventDispatcher->dispatch(new ScopeChangeEvent($baseSiteAccess), MVCEvents::CONFIG_SCOPE_RESTORE);
- $service->onConfigScopeRestore(new ScopeChangeEvent($baseSiteAccess));
self::assertSame($baseSiteAccess, $service->getCurrent());
}
@@ -160,7 +220,8 @@ public function testGetSiteAccess(): void
);
$service = new SiteAccessService(
$staticSiteAccessProvider,
- self::createStub(ConfigResolverInterface::class)
+ self::createStub(ConfigResolverInterface::class),
+ new EventDispatcher()
);
self::assertEquals(
@@ -177,7 +238,8 @@ public function testGetSiteAccessThrowsNotFoundException(): void
);
$service = new SiteAccessService(
$staticSiteAccessProvider,
- self::createStub(ConfigResolverInterface::class)
+ self::createStub(ConfigResolverInterface::class),
+ new EventDispatcher()
);
$this->expectException(NotFoundException::class);
@@ -213,10 +275,26 @@ public function testGetFirstSiteAccessesRelation(): void
);
}
+ private function createService(?EventDispatcherInterface $eventDispatcher = null): SiteAccessService
+ {
+ $eventDispatcher ??= new EventDispatcher();
+ $service = new SiteAccessService($this->provider, $this->configResolver, $eventDispatcher);
+ $eventDispatcher->addSubscriber($service);
+
+ return $service;
+ }
+
+ private function createSiteAccessMatchEvent(SiteAccess $siteAccess, int $requestType): PostSiteAccessMatchEvent
+ {
+ return new PostSiteAccessMatchEvent($siteAccess, new Request(), $requestType);
+ }
+
private function getSiteAccessService(): SiteAccessService
{
- $siteAccessService = new SiteAccessService($this->provider, $this->configResolver);
- $siteAccessService->setSiteAccess($this->siteAccess);
+ $siteAccessService = $this->createService();
+ $siteAccessService->onSiteAccessMatch(
+ $this->createSiteAccessMatchEvent($this->siteAccess, HttpKernelInterface::MAIN_REQUEST)
+ );
return $siteAccessService;
}