From 55a462d905889941ed630dca197bf5766e048552 Mon Sep 17 00:00:00 2001 From: Frank de Jonge Date: Wed, 5 Oct 2022 16:39:28 +0200 Subject: [PATCH] Added public URLs for WebDAV --- docker-compose.yml | 1 + .../FilesystemAdapterTestCase.php | 98 ++++++++++++------- src/AwsS3V3/AwsS3V3AdapterTest.php | 15 --- src/AzureBlobStorage/AzureBlobStorageTest.php | 16 --- .../GoogleCloudStorageAdapterTest.php | 15 --- src/WebDAV/WebDAVAdapter.php | 8 +- 6 files changed, 68 insertions(+), 85 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index d4ec25630..9a0463ac8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,7 @@ services: AUTH_TYPE: Digest USERNAME: alice PASSWORD: secret1234 + ANONYMOUS_METHODS: 'GET,OPTIONS' sftp: container_name: sftp restart: always diff --git a/src/AdapterTestUtilities/FilesystemAdapterTestCase.php b/src/AdapterTestUtilities/FilesystemAdapterTestCase.php index 715a4eaea..14a1f078f 100644 --- a/src/AdapterTestUtilities/FilesystemAdapterTestCase.php +++ b/src/AdapterTestUtilities/FilesystemAdapterTestCase.php @@ -4,7 +4,6 @@ namespace League\Flysystem\AdapterTestUtilities; -use const PHP_EOL; use Generator; use League\Flysystem\Config; use League\Flysystem\DirectoryAttributes; @@ -15,13 +14,17 @@ use League\Flysystem\UnableToReadFile; use League\Flysystem\UnableToRetrieveMetadata; use League\Flysystem\UnableToSetVisibility; +use League\Flysystem\UrlGeneration\PublicUrlGenerator; use League\Flysystem\Visibility; use PHPUnit\Framework\TestCase; use Throwable; + use function file_get_contents; use function is_resource; use function iterator_to_array; +use const PHP_EOL; + /** * @codeCoverageIgnore */ @@ -99,7 +102,7 @@ public function clearStorage(): void return; } - $this->runSetup(function () use ($adapter) { + $this->runSetup(function() use ($adapter) { /** @var StorageAttributes $item */ foreach ($adapter->listContents('', false) as $item) { if ($item->isDir()) { @@ -124,7 +127,7 @@ public function clearCustomAdapter(): void */ public function writing_and_reading_with_string(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); $adapter->write('path.txt', 'contents', new Config()); @@ -141,7 +144,7 @@ public function writing_and_reading_with_string(): void */ public function writing_a_file_with_a_stream(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); $writeStream = stream_with_contents('contents'); @@ -163,7 +166,7 @@ public function writing_a_file_with_a_stream(): void */ public function writing_and_reading_files_with_special_path(string $path): void { - $this->runScenario(function () use ($path) { + $this->runScenario(function() use ($path) { $adapter = $this->adapter(); $adapter->write($path, 'contents', new Config()); @@ -196,7 +199,7 @@ public function filenameProvider(): Generator */ public function writing_a_file_with_an_empty_stream(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); $writeStream = stream_with_contents(''); @@ -222,7 +225,7 @@ public function reading_a_file(): void { $this->givenWeHaveAnExistingFile('path.txt', 'contents'); - $this->runScenario(function () { + $this->runScenario(function() { $contents = $this->adapter()->read('path.txt'); $this->assertEquals('contents', $contents); @@ -236,7 +239,7 @@ public function reading_a_file_with_a_stream(): void { $this->givenWeHaveAnExistingFile('path.txt', 'contents'); - $this->runScenario(function () { + $this->runScenario(function() { $readStream = $this->adapter()->readStream('path.txt'); $contents = stream_get_contents($readStream); @@ -251,7 +254,7 @@ public function reading_a_file_with_a_stream(): void */ public function overwriting_a_file(): void { - $this->runScenario(function () { + $this->runScenario(function() { $this->givenWeHaveAnExistingFile('path.txt', 'contents', ['visibility' => Visibility::PUBLIC]); $adapter = $this->adapter(); @@ -269,7 +272,7 @@ public function overwriting_a_file(): void */ public function deleting_a_file(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); $this->givenWeHaveAnExistingFile('path.txt', 'contents'); @@ -285,7 +288,7 @@ public function deleting_a_file(): void */ public function listing_contents_shallow(): void { - $this->runScenario(function () { + $this->runScenario(function() { $this->givenWeHaveAnExistingFile('some/0-path.txt', 'contents'); $this->givenWeHaveAnExistingFile('some/1-nested/path.txt', 'contents'); @@ -313,7 +316,7 @@ public function listing_contents_shallow(): void */ public function checking_if_a_non_existing_directory_exists(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); self::assertFalse($adapter->directoryExists('this-does-not-exist.php')); }); @@ -324,7 +327,7 @@ public function checking_if_a_non_existing_directory_exists(): void */ public function checking_if_a_directory_exists_after_writing_a_file(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); $this->givenWeHaveAnExistingFile('existing-directory/file.txt'); self::assertTrue($adapter->directoryExists('existing-directory')); @@ -336,7 +339,7 @@ public function checking_if_a_directory_exists_after_writing_a_file(): void */ public function checking_if_a_directory_exists_after_creating_it(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); $adapter->createDirectory('explicitly-created-directory', new Config()); self::assertTrue($adapter->directoryExists('explicitly-created-directory')); @@ -352,7 +355,7 @@ public function checking_if_a_directory_exists_after_creating_it(): void */ public function listing_contents_recursive(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); $adapter->createDirectory('path', new Config()); $adapter->write('path/file.txt', 'string', new Config()); @@ -378,7 +381,7 @@ protected function formatIncorrectListingCount(array $items): string protected function givenWeHaveAnExistingFile(string $path, string $contents = 'contents', array $config = []): void { - $this->runSetup(function () use ($path, $contents, $config) { + $this->runSetup(function() use ($path, $contents, $config) { $this->adapter()->write($path, $contents, new Config($config)); }); } @@ -391,7 +394,7 @@ public function fetching_file_size(): void $adapter = $this->adapter(); $this->givenWeHaveAnExistingFile('path.txt', 'contents'); - $this->runScenario(function () use ($adapter) { + $this->runScenario(function() use ($adapter) { $attributes = $adapter->fileSize('path.txt'); $this->assertInstanceOf(FileAttributes::class, $attributes); $this->assertEquals(8, $attributes->fileSize()); @@ -403,7 +406,7 @@ public function fetching_file_size(): void */ public function setting_visibility(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); $this->givenWeHaveAnExistingFile('path.txt', 'contents', [Config::OPTION_VISIBILITY => Visibility::PUBLIC]); @@ -428,7 +431,7 @@ public function fetching_file_size_of_a_directory(): void $adapter = $this->adapter(); - $this->runScenario(function () use ($adapter) { + $this->runScenario(function() use ($adapter) { $adapter->createDirectory('path', new Config()); $adapter->fileSize('path/'); }); @@ -441,7 +444,7 @@ public function fetching_file_size_of_non_existing_file(): void { $this->expectException(UnableToRetrieveMetadata::class); - $this->runScenario(function () { + $this->runScenario(function() { $this->adapter()->fileSize('non-existing-file.txt'); }); } @@ -453,7 +456,7 @@ public function fetching_last_modified_of_non_existing_file(): void { $this->expectException(UnableToRetrieveMetadata::class); - $this->runScenario(function () { + $this->runScenario(function() { $this->adapter()->lastModified('non-existing-file.txt'); }); } @@ -465,7 +468,7 @@ public function fetching_visibility_of_non_existing_file(): void { $this->expectException(UnableToRetrieveMetadata::class); - $this->runScenario(function () { + $this->runScenario(function() { $this->adapter()->visibility('non-existing-file.txt'); }); } @@ -475,7 +478,7 @@ public function fetching_visibility_of_non_existing_file(): void */ public function fetching_the_mime_type_of_an_svg_file(): void { - $this->runScenario(function () { + $this->runScenario(function() { $this->givenWeHaveAnExistingFile('file.svg', file_get_contents(__DIR__ . '/test_files/flysystem.svg')); $mimetype = $this->adapter()->mimeType('file.svg')->mimeType(); @@ -491,7 +494,7 @@ public function fetching_mime_type_of_non_existing_file(): void { $this->expectException(UnableToRetrieveMetadata::class); - $this->runScenario(function () { + $this->runScenario(function() { $this->adapter()->mimeType('non-existing-file.txt'); }); } @@ -508,7 +511,7 @@ public function fetching_unknown_mime_type_of_a_file(): void $this->expectException(UnableToRetrieveMetadata::class); - $this->runScenario(function () { + $this->runScenario(function() { $this->adapter()->mimeType('unknown-mime-type.md5'); }); } @@ -521,7 +524,7 @@ public function listing_a_toplevel_directory(): void $this->givenWeHaveAnExistingFile('path1.txt'); $this->givenWeHaveAnExistingFile('path2.txt'); - $this->runScenario(function () { + $this->runScenario(function() { $contents = iterator_to_array($this->adapter()->listContents('', true)); $this->assertCount(2, $contents); @@ -533,7 +536,7 @@ public function listing_a_toplevel_directory(): void */ public function writing_and_reading_with_streams(): void { - $this->runScenario(function () { + $this->runScenario(function() { $writeStream = stream_with_contents('contents'); $adapter = $this->adapter(); @@ -557,7 +560,7 @@ public function setting_visibility_on_a_file_that_does_not_exist(): void { $this->expectException(UnableToSetVisibility::class); - $this->runScenario(function () { + $this->runScenario(function() { $this->adapter()->setVisibility('this-path-does-not-exists.txt', Visibility::PRIVATE); }); } @@ -567,7 +570,7 @@ public function setting_visibility_on_a_file_that_does_not_exist(): void */ public function copying_a_file(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); $adapter->write( 'source.txt', @@ -589,7 +592,7 @@ public function copying_a_file(): void */ public function copying_a_file_again(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); $adapter->write( 'source.txt', @@ -611,7 +614,7 @@ public function copying_a_file_again(): void */ public function moving_a_file(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); $adapter->write( 'source.txt', @@ -639,7 +642,7 @@ public function reading_a_file_that_does_not_exist(): void { $this->expectException(UnableToReadFile::class); - $this->runScenario(function () { + $this->runScenario(function() { $this->adapter()->read('path.txt'); }); } @@ -651,7 +654,7 @@ public function moving_a_file_that_does_not_exist(): void { $this->expectException(UnableToMoveFile::class); - $this->runScenario(function () { + $this->runScenario(function() { $this->adapter()->move('source.txt', 'destination.txt', new Config()); }); } @@ -674,7 +677,7 @@ public function trying_to_delete_a_non_existing_file(): void */ public function checking_if_files_exist(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); $fileExistsBefore = $adapter->fileExists('some/path.txt'); $adapter->write('some/path.txt', 'contents', new Config()); @@ -690,7 +693,7 @@ public function checking_if_files_exist(): void */ public function fetching_last_modified(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); $adapter->write('path.txt', 'contents', new Config()); @@ -728,7 +731,7 @@ public function failing_to_read_a_non_existing_file(): void */ public function creating_a_directory(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); $adapter->createDirectory('creating_a_directory/path', new Config()); @@ -751,7 +754,7 @@ public function creating_a_directory(): void */ public function copying_a_file_with_collision(): void { - $this->runScenario(function () { + $this->runScenario(function() { $adapter = $this->adapter(); $adapter->write('path.txt', 'new contents', new Config()); $adapter->write('new-path.txt', 'contents', new Config()); @@ -765,9 +768,28 @@ public function copying_a_file_with_collision(): void protected function assertFileExistsAtPath(string $path): void { - $this->runScenario(function () use ($path) { + $this->runScenario(function() use ($path) { $fileExists = $this->adapter()->fileExists($path); $this->assertTrue($fileExists); }); } + + /** + * @test + */ + public function generating_a_public_url(): void + { + $adapter = $this->adapter(); + + if ( ! $adapter instanceof PublicUrlGenerator) { + $this->markTestSkipped('Adapter does not supply public URls'); + } + + $adapter->write('some/path.txt', 'public contents', new Config(['visibility' => 'public'])); + + $url = $adapter->publicUrl('some/path.txt', new Config()); + $contents = file_get_contents($url); + + self::assertEquals('public contents', $contents); + } } diff --git a/src/AwsS3V3/AwsS3V3AdapterTest.php b/src/AwsS3V3/AwsS3V3AdapterTest.php index 743893a9c..14d047d3f 100644 --- a/src/AwsS3V3/AwsS3V3AdapterTest.php +++ b/src/AwsS3V3/AwsS3V3AdapterTest.php @@ -260,21 +260,6 @@ public function failing_to_check_for_file_existence(): void $adapter->fileExists('something-that-does-exist.txt'); } - /** - * @test - */ - public function generating_a_public_url(): void - { - /** @var AwsS3V3Adapter $adapter */ - $adapter = $this->adapter(); - $adapter->write('some/path.txt', 'public contents', new Config(['visibility' => 'public'])); - - $url = $adapter->publicUrl('some/path.txt', new Config()); - $contents = file_get_contents($url); - - self::assertEquals('public contents', $contents); - } - /** * @test * @dataProvider casesWhereHttpStreamingInfluencesSeekability diff --git a/src/AzureBlobStorage/AzureBlobStorageTest.php b/src/AzureBlobStorage/AzureBlobStorageTest.php index 745849bb4..e75d72487 100644 --- a/src/AzureBlobStorage/AzureBlobStorageTest.php +++ b/src/AzureBlobStorage/AzureBlobStorageTest.php @@ -11,7 +11,6 @@ use League\Flysystem\Visibility; use MicrosoftAzure\Storage\Blob\BlobRestProxy; -use function file_get_contents; use function getenv; /** @@ -203,19 +202,4 @@ public function creating_a_directory(): void { $this->markTestSkipped('This adapter does not support creating directories'); } - - /** - * @test - */ - public function generating_a_public_url(): void - { - /** @var AzureBlobStorageAdapter $adapter */ - $adapter = $this->adapter(); - $adapter->write('some/path.txt', 'public contents', new Config(['visibility' => 'public'])); - - $url = $adapter->publicUrl('some/path.txt', new Config()); - $contents = file_get_contents($url); - - self::assertEquals('public contents', $contents); - } } diff --git a/src/GoogleCloudStorage/GoogleCloudStorageAdapterTest.php b/src/GoogleCloudStorage/GoogleCloudStorageAdapterTest.php index 96420abdc..6047a8a96 100644 --- a/src/GoogleCloudStorage/GoogleCloudStorageAdapterTest.php +++ b/src/GoogleCloudStorage/GoogleCloudStorageAdapterTest.php @@ -163,19 +163,4 @@ public function failing_to_retrieve_visibility(): void $adapter->visibility('filename.txt'); } - - /** - * @test - */ - public function generating_a_public_url(): void - { - /** @var GoogleCloudStorageAdapter $adapter */ - $adapter = $this->adapter(); - $adapter->write('some/path.txt', 'public contents', new Config(['visibility' => 'public'])); - - $url = $adapter->publicUrl('some/path.txt', new Config()); - $contents = file_get_contents($url); - - self::assertEquals('public contents', $contents); - } } diff --git a/src/WebDAV/WebDAVAdapter.php b/src/WebDAV/WebDAVAdapter.php index 8742f3028..d3f0756d8 100644 --- a/src/WebDAV/WebDAVAdapter.php +++ b/src/WebDAV/WebDAVAdapter.php @@ -20,6 +20,7 @@ use League\Flysystem\UnableToRetrieveMetadata; use League\Flysystem\UnableToSetVisibility; use League\Flysystem\UnableToWriteFile; +use League\Flysystem\UrlGeneration\PublicUrlGenerator; use RuntimeException; use Sabre\DAV\Client; use Sabre\DAV\Xml\Property\ResourceType; @@ -35,7 +36,7 @@ use function implode; use function ltrim; -class WebDAVAdapter implements FilesystemAdapter +class WebDAVAdapter implements FilesystemAdapter, PublicUrlGenerator { public const ON_VISIBILITY_THROW_ERROR = 'throw'; public const ON_VISIBILITY_IGNORE = 'ignore'; @@ -437,4 +438,9 @@ private function propFind(string $path, string $section, string $property): mixe throw UnableToRetrieveMetadata::create($path, $section, $exception->getMessage(), $exception); } } + + public function publicUrl(string $path, Config $config): string + { + return $this->client->getAbsoluteUrl($this->encodePath($this->prefixer->prefixPath($path))); + } }