diff --git a/typo3/sysext/core/Classes/Resource/Driver/AbstractDriver.php b/typo3/sysext/core/Classes/Resource/Driver/AbstractDriver.php index c5d8bfdd9d5e78fd597ebd6cf16b896b83003647..487399b8b50c437a7c7cd1933f0981c7d6d76248 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 0114b94b181ca216bf4ec0d7ea743c47d58b97d3..afb2ee6c9f335190e5bdd38fd89b8b27278c83cd 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 d14d8da65a36cd12f10e3ffeda50694026b4cb5f..3dca8eb702469aedda69e53bcf160c99492f2702 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 c232af1fc3daa7197c630f7273c5ad489a91cfce..ec84426eab961dcd0ffd2ec1aa7f61ece9582443 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 5b7ae29a3ff5d2c2d08ce0fd8353d2e91ad40573..4ea00e03b52a7548d6b15ef9b790c26cea660694 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 */