From 89d110914c09ac2c75a35596b3b443b234f3aa41 Mon Sep 17 00:00:00 2001 From: Denny Lubitz Date: Wed, 8 Jan 2025 21:52:09 +0100 Subject: [PATCH 1/2] FEATURE: Neos 9.0 compatibility --- Classes/Aspects/ContentCacheAspect.php | 42 +--- Classes/Controller/VarnishCacheController.php | 147 ++++++------- .../CacheControlHeaderComponent.php | 66 +++--- Classes/Package.php | 30 --- Classes/Service/CacheTagService.php | 14 +- .../Service/ContentCacheFlusherService.php | 194 ++---------------- Configuration/Settings.Neos.yaml | 2 + Documentation/Index.rst | 10 +- .../Private/Templates/VarnishCache/Index.html | 2 +- .../Templates/VarnishCache/SearchForNode.html | 37 ++-- .../Libraries/jquery/jquery-3.6.0.min.js | 2 + composer.json | 5 +- 12 files changed, 162 insertions(+), 389 deletions(-) delete mode 100644 Classes/Package.php create mode 100644 Resources/Public/Libraries/jquery/jquery-3.6.0.min.js diff --git a/Classes/Aspects/ContentCacheAspect.php b/Classes/Aspects/ContentCacheAspect.php index 7a1b57e..dbf4f75 100644 --- a/Classes/Aspects/ContentCacheAspect.php +++ b/Classes/Aspects/ContentCacheAspect.php @@ -3,46 +3,31 @@ namespace Flowpack\Varnish\Aspects; -use Flowpack\Varnish\Service\CacheTagService; use Flowpack\Varnish\Service\VarnishBanService; use Neos\Flow\Annotations as Flow; use Neos\Flow\Aop\JoinPointInterface; use Neos\Flow\Configuration\Exception\InvalidConfigurationException; use Neos\Flow\Log\Utility\LogEnvironment; use Neos\Flow\Mvc\Exception\StopActionException; +use Neos\Fusion\Core\Runtime; use Neos\Fusion\Exception; use Neos\Fusion\Exception\RuntimeException; use Neos\Utility\Exception\PropertyNotAccessibleException; use Neos\Utility\ObjectAccess; -use Neos\Fusion\Core\Runtime; use Psr\Log\LoggerInterface; /** * Advice the RuntimeContentCache to check for uncached segments that should prevent caching - * - * @Flow\Aspect - * @Flow\Scope("singleton") */ +#[Flow\Aspect] +#[Flow\Scope("singleton")] class ContentCacheAspect { + #[Flow\Inject] + protected LoggerInterface $logger; - /** - * @Flow\Inject - * @var LoggerInterface - */ - protected $logger; - - /** - * @Flow\Inject - * @var VarnishBanService - */ - protected $varnishBanService; - - /** - * @Flow\Inject - * @var CacheTagService - */ - protected $cacheTagService; + #[Flow\Inject] + protected VarnishBanService $varnishBanService; /** * @var bool @@ -53,7 +38,6 @@ class ContentCacheAspect * Advice for uncached segments when rendering the initial output (without replacing an uncached marker in cached output) * * @Flow\AfterReturning("setting(Flowpack.Varnish.enabled) && method(Neos\Fusion\Core\Cache\RuntimeContentCache->postProcess())") - * @param JoinPointInterface $joinPoint * @throws PropertyNotAccessibleException * @throws InvalidConfigurationException * @throws StopActionException @@ -82,7 +66,6 @@ public function registerCreateUncached(JoinPointInterface $joinPoint): void * Advice for uncached segments when rendering from a cached version * * @Flow\AfterReturning("setting(Flowpack.Varnish.enabled) && method(Neos\Fusion\Core\Cache\RuntimeContentCache->evaluateUncached())") - * @param JoinPointInterface $joinPoint * @throws PropertyNotAccessibleException * @throws InvalidConfigurationException * @throws StopActionException @@ -109,7 +92,6 @@ public function registerEvaluateUncached(JoinPointInterface $joinPoint): void * Advice for a disabled content cache (e.g. because an exception was handled) * * @Flow\AfterReturning("setting(Flowpack.Varnish.enabled) && method(Neos\Fusion\Core\Cache\RuntimeContentCache->setEnableContentCache())") - * @param JoinPointInterface $joinPoint */ public function registerDisableContentCache(JoinPointInterface $joinPoint): void { @@ -121,17 +103,15 @@ public function registerDisableContentCache(JoinPointInterface $joinPoint): void } /** - * @Flow\Before("setting(Flowpack.Varnish.enabled) && method(Neos\Neos\Fusion\Cache\ContentCacheFlusher->shutdownObject())") - * @param JoinPointInterface $joinPoint + * @Flow\Before("setting(Flowpack.Varnish.enabled) && method(Neos\Neos\Fusion\Cache\ContentCacheFlusher->flushTagsImmediately())") * * @throws PropertyNotAccessibleException */ - public function interceptContentCacheFlush(JoinPointInterface $joinPoint) + public function interceptContentCacheFlush(JoinPointInterface $joinPoint): void { - $object = $joinPoint->getProxy(); - $tags = array_keys(ObjectAccess::getProperty($object, 'tagsToFlush', true)); + $tags = array_keys($joinPoint->getMethodArgument('tagsToFlush')); if ($tags === []) { - return; + return; } $this->varnishBanService->banByTags($tags); diff --git a/Classes/Controller/VarnishCacheController.php b/Classes/Controller/VarnishCacheController.php index af21696..2780420 100644 --- a/Classes/Controller/VarnishCacheController.php +++ b/Classes/Controller/VarnishCacheController.php @@ -5,10 +5,13 @@ use Flowpack\Varnish\Service\ContentCacheFlusherService; use Flowpack\Varnish\Service\VarnishBanService; -use Neos\ContentRepository\Domain\Model\Node; -use Neos\ContentRepository\Domain\Service\NodeTypeManager; +use Neos\ContentRepository\Core\NodeType\NodeTypeNames; +use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindDescendantNodesFilter; +use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\NodeType\NodeTypeCriteria; +use Neos\ContentRepository\Core\SharedModel\Node\NodeAddress; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; +use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; -use Neos\Error\Messages\Message; use Neos\Flow\Http\Client\CurlEngine; use Neos\Flow\Mvc\View\JsonView; use Neos\FluidAdaptor\View\TemplateView; @@ -16,44 +19,10 @@ use Neos\Neos\Domain\Model\Domain; use Neos\Neos\Domain\Model\Site; use Neos\Neos\Domain\Repository\SiteRepository; -use Neos\Neos\Domain\Service\ContentContext; -use Neos\Neos\Domain\Service\ContentContextFactory; -use Neos\Neos\Domain\Service\ContentDimensionPresetSourceInterface; -use Neos\Neos\Domain\Service\NodeSearchService; +use Neos\Neos\Domain\Service\SiteNodeUtility; class VarnishCacheController extends AbstractModuleController { - - /** - * @Flow\Inject - * @var NodeTypeManager - */ - protected $nodeTypeManager; - - /** - * @Flow\Inject - * @var ContentContextFactory - */ - protected $contextFactory; - - /** - * @Flow\Inject - * @var NodeSearchService - */ - protected $nodeSearchService; - - /** - * @Flow\Inject - * @var SiteRepository - */ - protected $siteRepository; - - /** - * @Flow\Inject - * @var ContentDimensionPresetSourceInterface - */ - protected $contentDimensionPresetSource; - /** * @var array */ @@ -62,6 +31,18 @@ class VarnishCacheController extends AbstractModuleController 'json' => JsonView::class, ]; + #[Flow\Inject] + protected SiteRepository $siteRepository; + + #[Flow\Inject] + protected ContentRepositoryRegistry $contentRepositoryRegistry; + + #[Flow\Inject] + protected SiteNodeUtility $siteNodeUtility; + + #[Flow\Inject] + protected ContentCacheFlusherService $contentCacheFlusherService; + public function indexAction(): void { $this->view->assign('activeSites', $this->siteRepository->findOnline()); @@ -71,7 +52,6 @@ public function indexAction(): void * @param string $searchWord * @param Site $selectedSite * @return void - * @throws \Neos\ContentRepository\Exception\NodeTypeNotFoundException * @throws \Neos\Flow\Mvc\Exception\ForwardException */ public function searchForNodeAction(string $searchWord = '', Site $selectedSite = null): void @@ -81,38 +61,53 @@ public function searchForNodeAction(string $searchWord = '', Site $selectedSite $this->forward('index'); } - $documentNodeTypes = $this->nodeTypeManager->getSubNodeTypes('Neos.Neos:Document'); - $shortcutNodeType = $this->nodeTypeManager->getNodeType('Neos.Neos:Shortcut'); - $nodeTypes = array_diff($documentNodeTypes, array($shortcutNodeType)); $sites = []; $activeSites = $this->siteRepository->findOnline(); foreach ($selectedSite ? [$selectedSite] : $activeSites as $site) { - /** @var Site $site */ - $contextProperties = [ - 'workspaceName' => 'live', - 'currentSite' => $site - ]; - $contentDimensionPresets = $this->contentDimensionPresetSource->getAllPresets(); - if (count($contentDimensionPresets) > 0) { - $mergedContentDimensions = []; - foreach ($contentDimensionPresets as $contentDimensionIdentifier => $contentDimension) { - $mergedContentDimensions[$contentDimensionIdentifier] = [$contentDimension['default']]; - foreach ($contentDimension['presets'] as $contentDimensionPreset) { - $mergedContentDimensions[$contentDimensionIdentifier] = array_merge($mergedContentDimensions[$contentDimensionIdentifier], $contentDimensionPreset['values']); - } - $mergedContentDimensions[$contentDimensionIdentifier] = array_values(array_unique($mergedContentDimensions[$contentDimensionIdentifier])); + $contentRepository = $this->contentRepositoryRegistry->get($site->getConfiguration()->contentRepositoryId); + $nodeTypeManager = $contentRepository->getNodeTypeManager(); + + $documentNodeTypes = $nodeTypeManager->getSubNodeTypes('Neos.Neos:Document'); + $shortcutNodeType = $nodeTypeManager->getNodeType('Neos.Neos:Shortcut'); + + $nodes = []; + + $contentDimensionSpacePoints = $contentRepository->getVariationGraph()->getDimensionSpacePoints(); + foreach ($contentDimensionSpacePoints as $contentDimensionSpacePoint) { + $currentSiteNode = $this->siteNodeUtility->findSiteNodeBySite( + $site, + WorkspaceName::forLive(), + $contentDimensionSpacePoint + ); + $subgraph = $this->contentRepositoryRegistry->subgraphForNode($currentSiteNode); + + $descendantNodes = $subgraph->findDescendantNodes( + $currentSiteNode->aggregateId, + filter: FindDescendantNodesFilter::create( + nodeTypes: NodeTypeCriteria::create( + NodeTypeNames::fromArray(array_map(static fn ($documentNodeType) => $documentNodeType->name, $documentNodeTypes)), + NodeTypeNames::fromArray([$shortcutNodeType->name])), + searchTerm: $searchWord) + ); + + + if ($descendantNodes->count()) { + $nodes[] = array_map(fn ($node) => [ + 'node' => $node, + 'nodeAddress' => NodeAddress::fromNode($node)->toJson(), + 'nodeTypeIcon' => $nodeTypeManager->getNodeType($node->nodeTypeName)->getConfiguration('ui.icon'), + 'nodeTypeLabel' => $nodeTypeManager->getNodeType($node->nodeTypeName)->getLabel(), + ], iterator_to_array($descendantNodes->getIterator())); } - $contextProperties['dimensions'] = $mergedContentDimensions; } - /** @var ContentContext $liveContext */ - $liveContext = $this->contextFactory->create($contextProperties); - $nodes = $this->nodeSearchService->findByProperties($searchWord, $nodeTypes, $liveContext, $liveContext->getCurrentSiteNode()); + if (count($nodes) > 0) { - $sites[$site->getNodeName()] = [ + $sites[$site->getNodeName()->value] = [ 'site' => $site, - 'nodes' => $nodes + 'nodes' => array_merge(...$nodes) ]; } + } $this->view->assignMultiple([ 'searchWord' => $searchWord, @@ -122,22 +117,19 @@ public function searchForNodeAction(string $searchWord = '', Site $selectedSite ]); } - /** - * @param Node $node - * @return void - * @throws \Neos\ContentRepository\Exception\NodeTypeNotFoundException - */ - public function purgeCacheAction(Node $node): void + public function purgeCacheAction(string $nodeAddress): void { - $service = new ContentCacheFlusherService(); - $service->flushForNode($node); + $nodeAddress = NodeAddress::fromJsonString($nodeAddress); + $contentRepository = $this->contentRepositoryRegistry->get($nodeAddress->contentRepositoryId); + $subgraph = $contentRepository->getContentSubgraph($nodeAddress->workspaceName, $nodeAddress->dimensionSpacePoint); + $node = $subgraph->findNodeById($nodeAddress->aggregateId); + + $this->contentCacheFlusherService->flushForNode($node); + $this->view->assign('value', true); } /** - * @param string $tags - * @param Site $site - * @return void * @throws \Neos\Flow\Mvc\Exception\StopActionException */ public function purgeCacheByTagsAction(string $tags, Site $site = null): void @@ -157,9 +149,6 @@ public function purgeCacheByTagsAction(string $tags, Site $site = null): void } /** - * @param Site $site - * @param string $contentType - * @return void * @throws \Neos\Flow\Mvc\Exception\StopActionException */ public function purgeAllVarnishCacheAction(Site $site = null, $contentType = null): void @@ -177,8 +166,6 @@ public function purgeAllVarnishCacheAction(Site $site = null, $contentType = nul } /** - * @param string $url - * @return void * @throws \Neos\Flow\Http\Client\CurlEngineException * @throws \Neos\Flow\Http\Exception */ @@ -194,13 +181,13 @@ public function checkUrlAction(string $url): void $engine->setOption(CURLOPT_SSL_VERIFYPEER, false); $engine->setOption(CURLOPT_SSL_VERIFYHOST, false); $response = $engine->sendRequest($request); - $this->view->assign('value', array( + $this->view->assign('value', [ 'statusCode' => $response->getStatusCode(), 'host' => parse_url($url, PHP_URL_HOST), 'url' => $url, 'headers' => array_map(function ($value) { return current($value); }, $response->getHeaders()) - )); + ]); } } diff --git a/Classes/Middleware/CacheControlHeaderComponent.php b/Classes/Middleware/CacheControlHeaderComponent.php index 9fddb81..05682a8 100644 --- a/Classes/Middleware/CacheControlHeaderComponent.php +++ b/Classes/Middleware/CacheControlHeaderComponent.php @@ -7,12 +7,13 @@ use Flowpack\Varnish\Cache\MetadataAwareStringFrontend; use Flowpack\Varnish\Service\CacheTagService; use Flowpack\Varnish\Service\TokenStorage; -use Neos\ContentRepository\Domain\Model\NodeInterface; +use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\Flow\Annotations as Flow; use Neos\Flow\Http\ServerRequestAttributes; use Neos\Flow\Log\Utility\LogEnvironment; use Neos\Flow\Mvc\ActionRequestFactory; use Neos\Flow\Property\PropertyMapper; +use Neos\Neos\Domain\NodeLabel\NodeLabelGeneratorInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; @@ -27,48 +28,33 @@ class CacheControlHeaderComponent implements MiddlewareInterface * @var array * @Flow\InjectConfiguration(path="cacheHeaders") */ - protected $cacheHeaderSettings; + protected array $cacheHeaderSettings; - /** - * @var ContentCacheAspect - * @Flow\Inject - */ - protected $contentCacheAspect; + #[Flow\Inject] + protected ContentCacheAspect $contentCacheAspect; - /** - * @var TokenStorage - * @Flow\Inject - */ - protected $tokenStorage; + #[Flow\Inject] + protected TokenStorage $tokenStorage; - /** - * @var LoggerInterface - * @Flow\Inject - */ - protected $logger; + #[Flow\Inject] + protected LoggerInterface $logger; - /** - * @var PropertyMapper - * @Flow\Inject - */ - protected $propertyMapper; + #[Flow\Inject] + protected PropertyMapper$propertyMapper; - /** - * @var CacheTagService - * @Flow\Inject - */ - protected $cacheTagService; + #[Flow\Inject] + protected CacheTagService $cacheTagService; /** * @var MetadataAwareStringFrontend */ protected $contentCacheFrontend; - /** - * @Flow\Inject(lazy=false) - * @var ActionRequestFactory - */ - protected $actionRequestFactory; + #[Flow\Inject(lazy: false)] + protected ActionRequestFactory $actionRequestFactory; + + #[Flow\Inject] + protected NodeLabelGeneratorInterface $nodeLabelGenerator; public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { @@ -92,32 +78,32 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface } try { - $node = $this->propertyMapper->convert($actionRequest->getArgument('node'), NodeInterface::class); + $node = $this->propertyMapper->convert($actionRequest->getArgument('node'), Node::class); } catch (\Exception $exception) { $this->logger->error(sprintf('The node argument was set to "%s", but it could not be converted into a node: %s', $actionRequest->getArgument('node'), $exception->getMessage())); return $response; } - if (!$node instanceof NodeInterface) { + if (!$node instanceof Node) { return $response; } - if ($node->getContext()->getWorkspaceName() !== 'live') { + if (!$node->workspaceName->isLive()) { return $response; } if ($node->hasProperty('disableVarnishCache') && $node->getProperty('disableVarnishCache') === true) { - $this->logger->debug(sprintf('Varnish cache headers skipped due to property "disableVarnishCache" for node "%s" (%s)', $node->getLabel(), $node->getPath()), LogEnvironment::fromMethodName(__METHOD__)); + $this->logger->debug(sprintf('Varnish cache headers skipped due to property "disableVarnishCache" for node "%s" (%s)', $this->nodeLabelGenerator->getLabel($node), $node->aggregateId->value), LogEnvironment::fromMethodName(__METHOD__)); return $response->withAddedHeader(self::HEADER_CACHE_CONTROL, 'no-cache'); } if ($this->contentCacheAspect->isEvaluatedUncached()) { - $this->logger->debug(sprintf('Varnish cache disabled due to uncachable content for node "%s" (%s)', $node->getLabel(), $node->getPath()), LogEnvironment::fromMethodName(__METHOD__)); + $this->logger->debug(sprintf('Varnish cache disabled due to uncachable content for node "%s" (%s)', $this->nodeLabelGenerator->getLabel($node), $node->aggregateId->value), LogEnvironment::fromMethodName(__METHOD__)); return $response->withAddedHeader(self::HEADER_CACHE_CONTROL, 'no-cache'); } - list($tags, $cacheLifetime) = $this->getCacheTagsAndLifetime(); + [$tags, $cacheLifetime] = $this->getCacheTagsAndLifetime(); if (count($tags) > 0) { $shortenedTags = $this->cacheTagService->shortenTags($tags); @@ -142,9 +128,9 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface if ($timeToLive !== null) { $response = $response->withAddedHeader(self::HEADER_CACHE_CONTROL, sprintf('public, s-maxage=%d', $timeToLive)); - $this->logger->debug(sprintf('Varnish cache enabled for node "%s" (%s) with max-age "%u"', $node->getLabel(), $node->getPath(), $timeToLive), LogEnvironment::fromMethodName(__METHOD__)); + $this->logger->debug(sprintf('Varnish cache enabled for node "%s" (%s) with max-age "%u"', $this->nodeLabelGenerator->getLabel($node), $node->aggregateId->value, $timeToLive), LogEnvironment::fromMethodName(__METHOD__)); } else { - $this->logger->debug(sprintf('Varnish cache headers not sent for node "%s" (%s) due to no max-age', $node->getLabel(), $node->getPath()), LogEnvironment::fromMethodName(__METHOD__)); + $this->logger->debug(sprintf('Varnish cache headers not sent for node "%s" (%s) due to no max-age', $this->nodeLabelGenerator->getLabel($node), $node->aggregateId->value), LogEnvironment::fromMethodName(__METHOD__)); } if ($this->cacheHeaderSettings['debug'] ?? false) { diff --git a/Classes/Package.php b/Classes/Package.php deleted file mode 100644 index d831936..0000000 --- a/Classes/Package.php +++ /dev/null @@ -1,30 +0,0 @@ -getSignalSlotDispatcher(); - $dispatcher->connect(ConfigurationManager::class, 'configurationManagerReady', function (ConfigurationManager $configurationManager) use ($dispatcher) { - $enabled = $configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'Flowpack.Varnish.enabled'); - if ((boolean)$enabled === true) { - $dispatcher->connect(PublishingService::class, 'nodePublished', ContentCacheFlusherService::class, 'flushForNode'); - } - }); - } -} diff --git a/Classes/Service/CacheTagService.php b/Classes/Service/CacheTagService.php index 8c1d1a4..c473a8f 100644 --- a/Classes/Service/CacheTagService.php +++ b/Classes/Service/CacheTagService.php @@ -5,23 +5,21 @@ use Neos\Flow\Annotations as Flow; -/** - * @Flow\Scope("singleton") - */ +#[Flow\Scope("singleton")] class CacheTagService { /** - * @var int + * @var array * @Flow\InjectConfiguration(path="cacheHeaders") */ protected $cacheHeaderConfiguration; /** + * @param array $tags + * @return array * @see \Neos\Fusion\Core\Cache\ContentCache::sanitizeTags() * - * @param array $tags - * @return array */ public function sanitizeTags(array $tags): array { @@ -37,8 +35,8 @@ public function sanitizeTags(array $tags): array * * Flowpack.Varnish.cacheHeaders.shortenCacheTags * * Flowpack.Varnish.cacheHeaders.cacheTagLength * - * @param array $tags - * @return array + * @param array $tags + * @return array */ public function shortenTags(array $tags = []): array { diff --git a/Classes/Service/ContentCacheFlusherService.php b/Classes/Service/ContentCacheFlusherService.php index cb5b3cd..1f5a852 100644 --- a/Classes/Service/ContentCacheFlusherService.php +++ b/Classes/Service/ContentCacheFlusherService.php @@ -3,188 +3,34 @@ namespace Flowpack\Varnish\Service; -use Neos\ContentRepository\Domain\Model\NodeData; -use Neos\ContentRepository\Domain\Model\NodeInterface; -use Neos\ContentRepository\Domain\Model\NodeType; -use Neos\ContentRepository\Domain\Repository\WorkspaceRepository; -use Neos\ContentRepository\Domain\Service\NodeTypeManager; -use Neos\ContentRepository\Exception\NodeTypeNotFoundException; +use Neos\ContentRepository\Core\Projection\ContentGraph\Node; +use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; -use Neos\Fusion\Core\Cache\ContentCache; -use Neos\Neos\Domain\Model\Domain; -use Neos\Neos\Domain\Model\Site; -use Neos\Neos\Domain\Service\ContentContext; -use Neos\Neos\Fusion\Helper\CachingHelper; +use Neos\Neos\Fusion\Cache\CacheFlushingStrategy; +use Neos\Neos\Fusion\Cache\ContentCacheFlusher; +use Neos\Neos\Fusion\Cache\FlushNodeAggregateRequest; -/** - * @Flow\Scope("singleton") - * - * @deprecated will be removed with 6.0 - */ +#[Flow\Scope("singleton")] class ContentCacheFlusherService { - /** - * @Flow\Inject - * @var VarnishBanService - */ - protected $varnishBanService; + #[Flow\Inject] + protected ContentRepositoryRegistry $contentRepositoryRegistry; - /** - * @var CachingHelper - */ - protected $cachingHelper; + #[Flow\Inject] + protected ContentCacheFlusher $contentCacheFlusher; - /** - * @Flow\Inject - * @var NodeTypeManager - */ - protected $nodeTypeManager; - - /** - * @var array - */ - protected $tagsToFlush = []; - - /** - * @var array - */ - protected $domainsToFlush = []; - - /** - * @param NodeInterface $node The node which has changed in some way - * @return void - * @throws NodeTypeNotFoundException - */ - public function flushForNode(NodeInterface $node): void - { - $this->generateCacheTags($node); - } - - /** - * @param NodeData $nodeData The node which has changed in some way - * @return void - * @throws NodeTypeNotFoundException - */ - public function flushForNodeData(NodeData $nodeData): void - { - $this->generateCacheTags($nodeData); - } - - /** - * Generates cache tags to be flushed for a node which is flushed on shutdown. - * - * @param NodeInterface|NodeData $node The node which has changed in some way - * @return void - * @throws NodeTypeNotFoundException - */ - protected function generateCacheTags(NodeInterface $node): void - { - $this->tagsToFlush[ContentCache::TAG_EVERYTHING] = 'which were tagged with "Everything".'; - - $workspaceHash = $this->getCachingHelper()->renderWorkspaceTagForContextNode('live'); - - $nodeIdentifier = $node->getIdentifier(); - - $this->generateCacheTagsForNodeIdentifier($workspaceHash .'_'. $nodeIdentifier); - $this->generateCacheTagsForNodeType($node->getNodeType()->getName(), $nodeIdentifier, $workspaceHash); - - $traversedNode = $node; - while ($traversedNode->getDepth() > 1) { - $traversedNode = $traversedNode->getParent(); - // Workaround for issue #56566 in Neos.ContentRepository - if ($traversedNode === null) { - break; - } - $this->generateCacheTagsForDescendantOf($workspaceHash . '_' . $traversedNode->getIdentifier()); - } - - if ($node instanceof NodeInterface && $node->getContext() instanceof ContentContext) { - /** @var Site $site */ - $site = $node->getContext()->getCurrentSite(); - if ($site->hasActiveDomains()) { - $domains = $site->getActiveDomains()->map(function (Domain $domain) { - return $domain->getHostname(); - })->toArray(); - $this->domainsToFlush = array_unique(array_merge($this->domainsToFlush, $domains)); - } - } - } - - /** - * @param string $cacheIdentifier - */ - protected function generateCacheTagsForNodeIdentifier(string $cacheIdentifier): void - { - $tagName = 'Node_' . $cacheIdentifier; - $this->tagsToFlush[$tagName] = sprintf('which were tagged with "%s" because node identifier "%s" has changed.', $tagName, $cacheIdentifier); - // Note, as we don't have a node here we cannot go up the structure. - $this->generateCacheTagsForDescendantOf($cacheIdentifier); - } - - /** - * @param string $cacheIdentifier - */ - protected function generateCacheTagsForDescendantOf(string $cacheIdentifier): void - { - $tagName = 'DescendantOf_' . $cacheIdentifier; - $this->tagsToFlush[$tagName] = sprintf('which were tagged with "%s" because node "%s" has changed.', $tagName, $cacheIdentifier); - } - - /** - * @param string $nodeTypeName - * @param string|null $referenceNodeIdentifier - * @param string $nodeTypePrefix - * - * @throws NodeTypeNotFoundException - */ - protected function generateCacheTagsForNodeType(string $nodeTypeName, string $referenceNodeIdentifier = null, string $nodeTypePrefix = ''): void - { - $nodeTypesToFlush = $this->getAllImplementedNodeTypeNames($this->nodeTypeManager->getNodeType($nodeTypeName)); - - if ($nodeTypePrefix !== '') { - $nodeTypePrefix = rtrim($nodeTypePrefix, '_') . '_'; - } - foreach ($nodeTypesToFlush as $nodeTypeNameToFlush) { - $tagName = 'NodeType_' . $nodeTypePrefix . $nodeTypeNameToFlush; - $this->tagsToFlush[$tagName] = sprintf('which were tagged with "%s" because node "%s" has changed and was of type "%s".', $tagName, $referenceNodeIdentifier ?: '', $nodeTypeName); - } - } - - /** - * Flush caches according to the previously registered node changes. - * - * @return void - */ - public function shutdownObject(): void - { - if (!empty($this->tagsToFlush)) { - $this->varnishBanService->banByTags(array_keys($this->tagsToFlush), $this->domainsToFlush); - } - } - - /** - * @param NodeType $nodeType - * @return array - */ - protected function getAllImplementedNodeTypeNames(NodeType $nodeType): array + public function flushForNode(Node $node): void { - $self = $this; - $types = array_reduce($nodeType->getDeclaredSuperTypes(), static function (array $types, NodeType $superType) use ($self) { - return array_merge($types, $self->getAllImplementedNodeTypeNames($superType)); - }, [$nodeType->getName()]); - $types = array_unique($types); - return $types; - } + $contentGraph = $this->contentRepositoryRegistry->get($node->contentRepositoryId)->getContentGraph($node->workspaceName); - /** - * @return CachingHelper - */ - protected function getCachingHelper(): CachingHelper - { - if (!$this->cachingHelper instanceof CachingHelper) { - $this->cachingHelper = new CachingHelper(); - } - return $this->cachingHelper; + $request = FlushNodeAggregateRequest::create( + $node->contentRepositoryId, + $node->workspaceName, + $node->aggregateId, + $node->nodeTypeName, + $contentGraph->findAncestorNodeAggregateIds($node->aggregateId), + ); + $this->contentCacheFlusher->flushNodeAggregate($request, CacheFlushingStrategy::IMMEDIATE); } } diff --git a/Configuration/Settings.Neos.yaml b/Configuration/Settings.Neos.yaml index 1d065de..39574be 100644 --- a/Configuration/Settings.Neos.yaml +++ b/Configuration/Settings.Neos.yaml @@ -16,6 +16,8 @@ Neos: description: 'Clear Varnish cache.' icon: 'fa fa-bolt' additionalResources: + javaScripts: + - 'resource://Flowpack.Varnish/Public/Libraries/jquery/jquery-3.6.0.min.js' styleSheets: - 'resource://Flowpack.Varnish/Public/Styles/Module.css' privilegeTarget: 'Flowpack.Varnish:BackendModuleClearCache' diff --git a/Documentation/Index.rst b/Documentation/Index.rst index 6b11306..a4739cd 100644 --- a/Documentation/Index.rst +++ b/Documentation/Index.rst @@ -27,7 +27,7 @@ There are several configuration options can/needs to be set: ``Flowpack.Varnish.reverseLookupPort`` accepts integer (defaults to ``NULL``) - Ignored cache tags can be used to ignore certain cache tags from being cleared at all (useful for optimizing) ``Flowpack.Varnish.ignoredCacheTags`` accepts array of strings (defaults to ``NULL``) - E.g. 'TYPO3.Neos:Document' which is used in 'TYPO3.Neos:Menu' elements + E.g. 'Neos.Neos:Document' which is used in 'Neos.Neos:Menu' elements - To disable the Varnish caching for deployment reasons you can set this option to ``FALSE`` ``Flowpack.Varnish.enabled`` accepts boolean value (defaults to ``TRUE``) @@ -69,10 +69,10 @@ Example:: ***Note*** For a page to be cached, it must not contains any uncached parts (e.g. plugins which are uncachable by default). When a node is published to the ``live`` workspace a ban request is send to the -Varnish proxy with the node's cache tags. This is done by listening to the ``nodePublished`` event from the -``PublishingService`` which calls ``flushForNode`` in ``ContentCacheFlusherService``. For custom flushing of the cache, -e.g. on node import, either use ``flushForNode`` or alternatively ``flushForNodeData`` if working directly with NodeData. -The ``ContentCacheFlusherService`` generates cache tags for all published nodes and during shutdown it will send one ban +Varnish proxy with the node's cache tags. + +For custom flushing of the cache, e.g. on node import, either use ``flushForNode``. +The ``ContentCacheFlusherService`` generates cache tags for all published nodes and will send one ban request, using the ``VarnishBanService``, containing all the tags to be cleared. The ``VarnishBanService`` has two methods ``banAll`` accepting ``domain`` & ``contentType`` (MIME type) and ``banByTags`` accepting ``tags`` and ``domain``. diff --git a/Resources/Private/Templates/VarnishCache/Index.html b/Resources/Private/Templates/VarnishCache/Index.html index 12fb3af..3dbd317 100644 --- a/Resources/Private/Templates/VarnishCache/Index.html +++ b/Resources/Private/Templates/VarnishCache/Index.html @@ -49,7 +49,7 @@
- +
diff --git a/Resources/Private/Templates/VarnishCache/SearchForNode.html b/Resources/Private/Templates/VarnishCache/SearchForNode.html index 1146bab..8352570 100644 --- a/Resources/Private/Templates/VarnishCache/SearchForNode.html +++ b/Resources/Private/Templates/VarnishCache/SearchForNode.html @@ -27,24 +27,25 @@ - - + + - - - + + + + - {node.label} + {neos:node.label(node: item.node)} - {neos:uri.node(node: node)} - + 2{neos:uri.node(node: item.node)} + Caching disabled - {f:if(condition: node.properties.cacheTimeToLive, then: ' {node.properties.cacheTimeToLive} s')} + {f:if(condition: item.node.properties.cacheTimeToLive, then: ' {item.node.properties.cacheTimeToLive} s')} - - -
+ + +
@@ -130,8 +131,8 @@ $('#url-check-loading', modalContent).hide(); console.error(jqXHR); var message = 'An error occurred while fetching additional information: ' + errorThrown; - if (window.Typo3Neos) { - window.Typo3Neos.Notification.error(message); + if (window.NeosCMS) { + window.NeosCMS.Notification.error(message); } else { alert(message); } @@ -143,8 +144,8 @@ button.html(''); $.ajax($(this).attr('href')).done(function(data, textStatus, jqXHR) { button.html(''); - if (window.Typo3Neos) { - window.Typo3Neos.Notification.ok('Cleared cache for node "' + button.data('node-label') + '"'); + if (window.NeosCMS) { + window.NeosCMS.Notification.ok('Cleared cache for node "' + button.data('node-label') + '"'); } else { alert(message); } @@ -152,8 +153,8 @@ button.html(''); console.error(jqXHR); var message = 'An error occurred while clearing the cache: ' + errorThrown; - if (window.Typo3Neos) { - window.Typo3Neos.Notification.error(message); + if (window.NeosCMS) { + window.NeosCMS.Notification.error(message); } else { alert(message); } diff --git a/Resources/Public/Libraries/jquery/jquery-3.6.0.min.js b/Resources/Public/Libraries/jquery/jquery-3.6.0.min.js new file mode 100644 index 0000000..c4c6022 --- /dev/null +++ b/Resources/Public/Libraries/jquery/jquery-3.6.0.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0 Date: Tue, 28 Jan 2025 12:32:49 +0100 Subject: [PATCH 2/2] TASK: Format code --- Classes/Middleware/CacheControlHeaderComponent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/Middleware/CacheControlHeaderComponent.php b/Classes/Middleware/CacheControlHeaderComponent.php index 05682a8..23e3081 100644 --- a/Classes/Middleware/CacheControlHeaderComponent.php +++ b/Classes/Middleware/CacheControlHeaderComponent.php @@ -40,7 +40,7 @@ class CacheControlHeaderComponent implements MiddlewareInterface protected LoggerInterface $logger; #[Flow\Inject] - protected PropertyMapper$propertyMapper; + protected PropertyMapper $propertyMapper; #[Flow\Inject] protected CacheTagService $cacheTagService;