From 1c797cc6d0b2197ff43d6c3330061f75a0dbc9cd Mon Sep 17 00:00:00 2001 From: Helmut Hummel <typo3@helhum.io> Date: Wed, 29 Apr 2020 12:30:31 +0200 Subject: [PATCH] [BUGFIX] Allow LocalDriver baseUri to be set to any value LocalDriver evaluates a configuration option baseUri, to use this URI instead of a derived one from the absolute base path of files. However there are currently limitations that need to be fixed to allow more complex setups: 1. baseUri is not defined in flexform, thus cannot be set from UI 2. baseUri isn't evaluated, when the absolute file path is within TYPO3's public path 3. baseUri is currently required to be a fully qualified URI (with scheme and host) This change addresses all three of them to cover the following use cases: 1. Provide a different hostname to serve the files from, even when they are located on the same machine. 2. Allow base URI without host name, for files that are on the same host, but in a folder not within TYPO3's root directory (e.g. due to special web server configuration) Resolves: #91232 Releases: master, 10.4 Change-Id: I7694661f9892c612489c68ef271f965df65744b6 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/64345 Reviewed-by: Markus Klein <markus.klein@typo3.org> Reviewed-by: Georg Ringer <georg.ringer@gmail.com> Tested-by: TYPO3com <noreply@typo3.com> Tested-by: Markus Klein <markus.klein@typo3.org> Tested-by: Georg Ringer <georg.ringer@gmail.com> --- .../Resource/Driver/AbstractDriver.php | 2 +- .../Classes/Resource/Driver/LocalDriver.php | 7 +- .../Resource/Driver/LocalDriverFlexForm.xml | 10 ++ .../Private/Language/locallang_mod_file.xlf | 6 + .../Unit/Resource/Driver/LocalDriverTest.php | 103 ++++++++++++++++++ 5 files changed, 123 insertions(+), 5 deletions(-) diff --git a/typo3/sysext/core/Classes/Resource/Driver/AbstractDriver.php b/typo3/sysext/core/Classes/Resource/Driver/AbstractDriver.php index c5d8bfdd9d5e..487399b8b50c 100644 --- a/typo3/sysext/core/Classes/Resource/Driver/AbstractDriver.php +++ b/typo3/sysext/core/Classes/Resource/Driver/AbstractDriver.php @@ -117,7 +117,7 @@ abstract class AbstractDriver implements DriverInterface */ public function hasCapability($capability) { - return ($this->capabilities & $capability) == $capability; + return ($this->capabilities & $capability) === $capability; } /******************* diff --git a/typo3/sysext/core/Classes/Resource/Driver/LocalDriver.php b/typo3/sysext/core/Classes/Resource/Driver/LocalDriver.php index 0114b94b181c..afb2ee6c9f33 100644 --- a/typo3/sysext/core/Classes/Resource/Driver/LocalDriver.php +++ b/typo3/sysext/core/Classes/Resource/Driver/LocalDriver.php @@ -131,7 +131,9 @@ class LocalDriver extends AbstractHierarchicalFilesystemDriver implements Stream { // only calculate baseURI if the storage does not enforce jumpUrl Script if ($this->hasCapability(ResourceStorage::CAPABILITY_PUBLIC)) { - if (GeneralUtility::isFirstPartOfStr($this->absoluteBasePath, Environment::getPublicPath())) { + if (!empty($this->configuration['baseUri'])) { + $this->baseUri = rtrim($this->configuration['baseUri'], '/') . '/'; + } elseif (GeneralUtility::isFirstPartOfStr($this->absoluteBasePath, Environment::getPublicPath())) { // use site-relative URLs $temporaryBaseUri = rtrim(PathUtility::stripPathSitePrefix($this->absoluteBasePath), '/'); if ($temporaryBaseUri !== '') { @@ -140,8 +142,6 @@ class LocalDriver extends AbstractHierarchicalFilesystemDriver implements Stream $temporaryBaseUri = implode('/', $uriParts) . '/'; } $this->baseUri = $temporaryBaseUri; - } elseif (isset($this->configuration['baseUri']) && GeneralUtility::isValidUrl($this->configuration['baseUri'])) { - $this->baseUri = rtrim($this->configuration['baseUri'], '/') . '/'; } } } @@ -186,7 +186,6 @@ class LocalDriver extends AbstractHierarchicalFilesystemDriver implements Stream * * @param string $identifier * @return string|null NULL if file is missing or deleted, the generated url otherwise - * @throws \TYPO3\CMS\Core\Resource\Exception */ public function getPublicUrl($identifier) { diff --git a/typo3/sysext/core/Configuration/Resource/Driver/LocalDriverFlexForm.xml b/typo3/sysext/core/Configuration/Resource/Driver/LocalDriverFlexForm.xml index d14d8da65a36..3dca8eb70246 100644 --- a/typo3/sysext/core/Configuration/Resource/Driver/LocalDriverFlexForm.xml +++ b/typo3/sysext/core/Configuration/Resource/Driver/LocalDriverFlexForm.xml @@ -33,6 +33,16 @@ </config> </TCEforms> </pathType> + <baseUri> + <TCEforms> + <label>LLL:EXT:core/Resources/Private/Language/locallang_mod_file.xlf:sys_file_storage.localDriverFlexform_baseUri</label> + <config> + <type>input</type> + <placeholder>LLL:EXT:core/Resources/Private/Language/locallang_mod_file.xlf:sys_file_storage.localDriverFlexform_baseUri_placeholder</placeholder> + <size>30</size> + </config> + </TCEforms> + </baseUri> <caseSensitive> <TCEforms> <label>LLL:EXT:core/Resources/Private/Language/locallang_mod_file.xlf:sys_file_storage.localDriverFlexform_caseSensitive</label> diff --git a/typo3/sysext/core/Resources/Private/Language/locallang_mod_file.xlf b/typo3/sysext/core/Resources/Private/Language/locallang_mod_file.xlf index c232af1fc3da..ec84426eab96 100644 --- a/typo3/sysext/core/Resources/Private/Language/locallang_mod_file.xlf +++ b/typo3/sysext/core/Resources/Private/Language/locallang_mod_file.xlf @@ -18,6 +18,12 @@ <trans-unit id="sys_file_storage.localDriverFlexform_basePath_placeholder" resname="sys_file_storage.localDriverFlexform_basePath_placeholder"> <source>Add storage path here. For example: myPath/folder/</source> </trans-unit> + <trans-unit id="sys_file_storage.localDriverFlexform_baseUri" resname="sys_file_storage.localDriverFlexform_baseUri"> + <source>Base URI</source> + </trans-unit> + <trans-unit id="sys_file_storage.localDriverFlexform_baseUri_placeholder" resname="sys_file_storage.localDriverFlexform_baseUri_placeholder"> + <source>Use this URI to build the public url of files, instead of deriving the URI from base path</source> + </trans-unit> <trans-unit id="sys_file_storage.localDriverFlexform_pathType" resname="sys_file_storage.localDriverFlexform_pathType"> <source>Path type</source> </trans-unit> diff --git a/typo3/sysext/core/Tests/Unit/Resource/Driver/LocalDriverTest.php b/typo3/sysext/core/Tests/Unit/Resource/Driver/LocalDriverTest.php index 5b7ae29a3ff5..4ea00e03b52a 100644 --- a/typo3/sysext/core/Tests/Unit/Resource/Driver/LocalDriverTest.php +++ b/typo3/sysext/core/Tests/Unit/Resource/Driver/LocalDriverTest.php @@ -26,6 +26,7 @@ use TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException; use TYPO3\CMS\Core\Resource\Exception\FileOperationErrorException; use TYPO3\CMS\Core\Resource\Exception\InvalidFileNameException; use TYPO3\CMS\Core\Resource\ResourceStorage; +use TYPO3\CMS\Core\Resource\ResourceStorageInterface; use TYPO3\CMS\Core\Tests\Unit\Resource\BaseTestCase; use TYPO3\CMS\Core\Tests\Unit\Resource\Driver\Fixtures\LocalDriverFilenameFilter; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -41,6 +42,11 @@ class LocalDriverTest extends BaseTestCase */ protected $resetSingletonInstances = true; + /** + * @var bool Reset changed Environment + */ + protected $backupEnvironment = true; + /** * @var LocalDriver */ @@ -181,6 +187,103 @@ class LocalDriverTest extends BaseTestCase self::assertStringNotContainsString('/../', $basePath); } + public function publicUrlIsCalculatedCorrectlyWithDifferentBasePathsAndBasUrisDataProvider(): array + { + return [ + 'no base uri, within public' => [ + '/files/', + '', + '/foo.txt', + true, + 'files/foo.txt', + ], + 'no base uri, within project' => [ + '/../files/', + '', + '/foo.txt', + false, + null, + ], + 'base uri with host, within public' => [ + '/files/', + 'https://host.tld/', + '/foo.txt', + true, + 'https://host.tld/foo.txt', + ], + 'base uri with host, within project' => [ + '/../files/', + 'https://host.tld/', + '/foo.txt', + true, + 'https://host.tld/foo.txt', + ], + 'base uri with path only, within public' => [ + '/files/', + 'assets/', + '/foo.txt', + true, + 'assets/foo.txt', + ], + 'base uri with path only, within project' => [ + '/../files/', + 'assets/', + '/foo.txt', + true, + 'assets/foo.txt', + ], + 'base uri with path only, within other public dir' => [ + '/../public/assets/', + 'assets/', + '/foo.txt', + true, + 'assets/foo.txt', + ], + ]; + } + + /** + * @param string $basePath + * @param string $baseUri + * @param string $fileName + * @param bool $expectedIsPublic + * @param string|null $expectedPublicUrl + * @test + * @dataProvider publicUrlIsCalculatedCorrectlyWithDifferentBasePathsAndBasUrisDataProvider + */ + public function publicUrlIsCalculatedCorrectlyWithDifferentBasePathsAndBasUris(string $basePath, string $baseUri, string $fileName, bool $expectedIsPublic, ?string $expectedPublicUrl): void + { + $testDir = $this->createRealTestdir(); + $projectPath = $testDir . '/app'; + $publicPath = $projectPath . '/public'; + $absoluteBaseDir = $publicPath . $basePath; + mkdir($projectPath); + mkdir($publicPath); + mkdir($absoluteBaseDir, 0777, true); + Environment::initialize( + Environment::getContext(), + true, + false, + $projectPath, + $publicPath, + Environment::getVarPath(), + Environment::getConfigPath(), + Environment::getCurrentScript(), + Environment::isUnix() ? 'UNIX' : 'WINDOWS' + ); + $driverConfiguration = [ + 'pathType' => 'relative', + 'basePath' => $basePath, + 'baseUri' => $baseUri, + ]; + + $subject = $this->createDriver($driverConfiguration); + + self::assertSame($expectedIsPublic, $subject->hasCapability(ResourceStorageInterface::CAPABILITY_PUBLIC)); + self::assertSame($fileName, $subject->createFile($fileName, '/')); + self::assertSame($expectedPublicUrl, $subject->getPublicUrl($fileName)); + } + /** * @test */ -- GitLab