diff --git a/Classes/Utility/FileUtility.php b/Classes/Utility/FileUtility.php index 7cbf7e82..66bdc3ca 100644 --- a/Classes/Utility/FileUtility.php +++ b/Classes/Utility/FileUtility.php @@ -14,6 +14,7 @@ use FriendsOfTYPO3\Headless\Event\EnrichFileDataEvent; use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Http\Message\ServerRequestInterface; +use TYPO3\CMS\Core\Configuration\Features; use TYPO3\CMS\Core\EventDispatcher\EventDispatcher; use TYPO3\CMS\Core\Http\NormalizedParams; use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection; @@ -31,7 +32,6 @@ class FileUtility { public const RETINA_RATIO = 2; public const LQIP_RATIO = 0.1; - protected ContentObjectRenderer $contentObjectRenderer; protected RendererRegistry $rendererRegistry; protected ImageService $imageService; @@ -42,13 +42,15 @@ class FileUtility * @var array> */ protected array $errors = []; + protected Features $features; public function __construct( ?ContentObjectRenderer $contentObjectRenderer = null, ?RendererRegistry $rendererRegistry = null, ?ImageService $imageService = null, ?ServerRequestInterface $serverRequest = null, - ?EventDispatcherInterface $eventDispatcher = null + ?EventDispatcherInterface $eventDispatcher = null, + ?Features $features = null ) { $this->contentObjectRenderer = $contentObjectRenderer ?? GeneralUtility::makeInstance(ContentObjectRenderer::class); @@ -56,6 +58,7 @@ public function __construct( $this->imageService = $imageService ?? GeneralUtility::makeInstance(ImageService::class); $this->serverRequest = $serverRequest ?? ($GLOBALS['TYPO3_REQUEST'] ?? null); $this->eventDispatcher = $eventDispatcher ?? GeneralUtility::makeInstance(EventDispatcher::class); + $this->features = $features ?? GeneralUtility::makeInstance(Features::class); } /** @@ -122,18 +125,32 @@ public function processFile( 'extension' => $fileReference->getProperty('extension'), ]; - return [ - 'publicUrl' => $publicUrl, - 'properties' => $this->eventDispatcher->dispatch( - new EnrichFileDataEvent( - $originalFileReference, - $fileReference, - array_merge( - $originalProperties, - $processedProperties - ) + $event = $this->eventDispatcher->dispatch( + new EnrichFileDataEvent( + $originalFileReference, + $fileReference, + array_merge( + $originalProperties, + $processedProperties ) - )->getProperties(), + ) + ); + + $cacheBuster = ''; + + if ($this->features->isFeatureEnabled('headless.assetsCacheBusting') && $event->getProperties()['type'] !== 'video') { + $modified = $event->getProcessed()->getProperty('modification_date'); + + if (!$modified) { + $modified = $event->getProcessed()->getProperty('tstamp'); + } + + $cacheBuster = '?' . $modified; + } + + return [ + 'publicUrl' => $publicUrl . $cacheBuster, + 'properties' => $event->getProperties(), ]; } diff --git a/Classes/Utility/UrlUtility.php b/Classes/Utility/UrlUtility.php index 84c49352..cd0c9746 100644 --- a/Classes/Utility/UrlUtility.php +++ b/Classes/Utility/UrlUtility.php @@ -25,7 +25,6 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; use function array_merge; -use function count; use function rtrim; use function strpos; @@ -186,7 +185,9 @@ private function resolveWithVariants( array $variants = [], string $returnField = 'frontendBase' ): string { - if (count($variants) === 0) { + $frontendUrl = rtrim($frontendUrl, '/'); + + if ($variants === []) { return $frontendUrl; } diff --git a/Classes/ViewHelpers/Format/Json/DecodeViewHelper.php b/Classes/ViewHelpers/Format/Json/DecodeViewHelper.php index c6b253f5..f3e85eaa 100644 --- a/Classes/ViewHelpers/Format/Json/DecodeViewHelper.php +++ b/Classes/ViewHelpers/Format/Json/DecodeViewHelper.php @@ -33,6 +33,9 @@ public function render() $json = $this->arguments['json']; if ($json === null) { $json = $this->renderChildren(); + if ($json !== null) { + $json = trim($json); + } if (empty($json)) { return null; } diff --git a/Classes/XClass/Controller/FormFrontendController.php b/Classes/XClass/Controller/FormFrontendController.php index f97b8403..ebef8714 100644 --- a/Classes/XClass/Controller/FormFrontendController.php +++ b/Classes/XClass/Controller/FormFrontendController.php @@ -138,7 +138,7 @@ public function renderAction(): ResponseInterface $formFields = $formDefinition['renderables'][$currentPageIndex]['renderables']; // provides support for custom options providers (dynamic selects/radio/checkboxes) - $formFieldsNames = $this->generateFieldNamesAndReplaceCustomOptions($formFields, $formDefinition['identifier'], $formRuntime->getFormDefinition()); + $formFieldsNames = $this->generateFieldNamesAndReplaceCustomOptions($formFields, $formDefinition['identifier'], $formRuntime); if ($honeyPot) { $formFields[] = [ @@ -261,7 +261,7 @@ private function getNextPage(\TYPO3\CMS\Form\Domain\Runtime\FormRuntime $formRun * @param array $formFields * @return array */ - private function generateFieldNamesAndReplaceCustomOptions(array &$formFields, string $identifier, FormDefinition $definition): array + private function generateFieldNamesAndReplaceCustomOptions(array &$formFields, string $identifier, FormRuntime $formRuntime): array { $formFieldsNames = []; @@ -271,11 +271,11 @@ private function generateFieldNamesAndReplaceCustomOptions(array &$formFields, s is_array($field['renderables'])) { $formFieldsNames = array_merge( $formFieldsNames, - $this->generateFieldNamesAndReplaceCustomOptions($field['renderables'], $identifier, $definition) + $this->generateFieldNamesAndReplaceCustomOptions($field['renderables'], $identifier, $formRuntime) ); } else { if (!empty($field['properties']['customOptions'])) { - $customOptions = GeneralUtility::makeInstance($field['properties']['customOptions']); + $customOptions = GeneralUtility::makeInstance($field['properties']['customOptions'], $field, $formFields, $identifier, $formRuntime); if ($customOptions instanceof CustomOptionsInterface) { $field['properties']['options'] = $customOptions->get(); @@ -284,7 +284,7 @@ private function generateFieldNamesAndReplaceCustomOptions(array &$formFields, s unset($field['properties']['customOptions']); } - $defaultValue = $definition->getElementDefaultValueByIdentifier($field['identifier']); + $defaultValue = $formRuntime->getFormDefinition()->getElementDefaultValueByIdentifier($field['identifier']); if ($defaultValue) { $field['properties']['defaultValue'] = $defaultValue; diff --git a/Tests/Unit/Utility/FileUtilityTest.php b/Tests/Unit/Utility/FileUtilityTest.php index 6ea77b5e..d7cc00e9 100644 --- a/Tests/Unit/Utility/FileUtilityTest.php +++ b/Tests/Unit/Utility/FileUtilityTest.php @@ -73,6 +73,7 @@ public function testGetAbsoluteUrl(): void public function testProcessFile() { + $GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['headless.assetsCacheBusting'] = true; $fileData = [ 'uid' => 103, 'pid' => 0, @@ -361,7 +362,7 @@ protected function getImageServiceWithProcessedFile($file, $processedFile, $proc protected function getBaselineResultArrayForFile(): array { return [ - 'publicUrl' => 'https://test-frontend.tld/fileadmin/test-file.jpg', + 'publicUrl' => 'https://test-frontend.tld/fileadmin/test-file.jpg?1639061876', 'properties' => [ 'title' => null, @@ -396,7 +397,7 @@ protected function getBaselineResultArrayForFile(): array protected function getBaselineResultArrayForFileReference(): array { return [ - 'publicUrl' => 'https://test-frontend.tld/fileadmin/test-file.jpg', + 'publicUrl' => 'https://test-frontend.tld/fileadmin/test-file.jpg?1639061876', 'properties' => [ 'title' => null, @@ -482,6 +483,7 @@ protected function getFileReferenceBaselineData(): array 'width' => 526, 'uid_local' => 103, 'height' => 526, + 'tstamp' => 1639061876, ]; } diff --git a/Tests/Unit/Utility/UrlUtilityTest.php b/Tests/Unit/Utility/UrlUtilityTest.php index 6f6fcf54..4706e0d8 100644 --- a/Tests/Unit/Utility/UrlUtilityTest.php +++ b/Tests/Unit/Utility/UrlUtilityTest.php @@ -110,11 +110,11 @@ public function testFrontendUrlsWithBaseProductionAndLocalOverride(): void { $site = $this->prophesize(Site::class); $site->getConfiguration()->shouldBeCalled(3)->willReturn([ - 'base' => 'https://api.typo3.org', - 'frontendBase' => 'https://www.typo3.org', - 'frontendApiProxy' => 'https://www.typo3.org/headless', - 'frontendFileApi' => 'https://www.typo3.org/headless/fileadmin', - 'SpecialSitemapKey' => 'https://www.typo3.org/custom-sitemap', + 'base' => 'https://api.typo3.org/', + 'frontendBase' => 'https://www.typo3.org/', + 'frontendApiProxy' => 'https://www.typo3.org/headless/', + 'frontendFileApi' => 'https://www.typo3.org/headless/fileadmin/', + 'SpecialSitemapKey' => 'https://www.typo3.org/custom-sitemap/', 'languages' => [], 'baseVariants' => [ [ @@ -672,7 +672,7 @@ public function testEdgeCases() $urlUtility = new UrlUtility(null, $resolver->reveal(), $siteFinder, $request->reveal()); self::assertSame('https://frontend-domain-from-lang.tld', $urlUtility->getFrontendUrl()); - self::assertSame('https://frontend-domain-from-lang.tld/headless/', $urlUtility->getProxyUrl()); + self::assertSame('https://frontend-domain-from-lang.tld/headless', $urlUtility->getProxyUrl()); self::assertSame('https://frontend-domain-from-lang.tld/headless/fileadmin', $urlUtility->getStorageProxyUrl()); // configuration on language lvl with variants diff --git a/Tests/Unit/ViewHelpers/Format/Json/DecodeViewHelperTest.php b/Tests/Unit/ViewHelpers/Format/Json/DecodeViewHelperTest.php new file mode 100644 index 00000000..2579731a --- /dev/null +++ b/Tests/Unit/ViewHelpers/Format/Json/DecodeViewHelperTest.php @@ -0,0 +1,28 @@ +setArguments(['json' => null]); + $decodeViewHelper->setRenderChildrenClosure(function () { return "\n \n"; }); + $result = $decodeViewHelper->render(); + self::assertNull($result); + } +}