diff --git a/composer.json b/composer.json index 7e60994..6115dec 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,9 @@ }, "twistor/flysystem-stream-wrapper": { "PHP 8.2 support (https://www.drupal.org/project/flysystem/issues/3387094)": "https://raw.githubusercontent.com/City-of-Helsinki/drupal-module-helfi-azure-fs/ddb222622b92d1c2b7db975a84167a00579a1ad0/patches/3387094-add-context-property-to-stream-wrapper.patch" + }, + "drupal/flysystem": { + "UHF-10544 D10.3 support (https://drupal.org/i/3457193)": "https://raw.githubusercontent.com/City-of-Helsinki/drupal-module-helfi-azure-fs/82f0dc93d14357011d12d219f5d1641da7ae6960/patches/3457193.patch" } } } diff --git a/modules/flysystem_azure/flysystem_azure.info.yml b/modules/flysystem_azure/flysystem_azure.info.yml deleted file mode 100644 index ea7f87e..0000000 --- a/modules/flysystem_azure/flysystem_azure.info.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: Flysystem Azure -description: 'Provides drupal/flysystem_azure so composer package can be removed.' -type: module -core_version_requirement: ^9 || ^10 -package: Flysystem -dependencies: - - flysystem:flysystem diff --git a/patches/3457193.patch b/patches/3457193.patch new file mode 100644 index 0000000..00b1b5e --- /dev/null +++ b/patches/3457193.patch @@ -0,0 +1,101 @@ +diff --git a/flysystem.routing.yml b/flysystem.routing.yml +index 0eb3d82..0e0aeaf 100644 +--- a/flysystem.routing.yml ++++ b/flysystem.routing.yml +@@ -8,7 +8,7 @@ flysystem.files: + _access: 'TRUE' + scheme: '^[a-zA-Z0-9+.-]+$' + options: +- _maintenance_access: 'TRUE' ++ _maintenance_access: TRUE + + flysystem.serve: + path: '/_flysystem/{scheme}/{filepath}' +@@ -21,7 +21,7 @@ flysystem.serve: + scheme: '^[a-zA-Z0-9+.-]+$' + filepath: .+ + options: +- _maintenance_access: 'TRUE' ++ _maintenance_access: TRUE + + flysystem.config: + path: '/admin/config/media/file-system/flysystem' +diff --git a/src/Routing/FlysystemRoutes.php b/src/Routing/FlysystemRoutes.php +index d1ad705..fabf2a1 100644 +--- a/src/Routing/FlysystemRoutes.php ++++ b/src/Routing/FlysystemRoutes.php +@@ -76,6 +76,25 @@ class FlysystemRoutes implements ContainerInjectionInterface { + $all_settings = Settings::get('flysystem', []); + + foreach ($this->factory->getSchemes() as $scheme) { ++ if ($this->moduleHandler->moduleExists('image')) { ++ // Internal image route. ++ $routes['flysystem.' . $scheme . '.image_style'] = new Route( ++ '/_flysystem/styles/{image_style}/{scheme}', ++ [ ++ '_controller' => 'Drupal\image\Controller\ImageStyleDownloadController::deliver', ++ 'required_derivative_scheme' => $scheme, ++ '_disable_route_normalizer' => TRUE, ++ ], ++ [ ++ '_access' => 'TRUE', ++ 'scheme' => '^[a-zA-Z0-9+.-]+$', ++ ], ++ [ ++ '_maintenance_access' => TRUE, ++ ] ++ ); ++ } ++ + $settings = $all_settings[$scheme]; + + if ($settings['driver'] !== 'local' || empty($settings['config']['public'])) { +@@ -99,7 +118,7 @@ class FlysystemRoutes implements ContainerInjectionInterface { + '_access' => 'TRUE', + ], + [ +- '_maintenance_access' => 'TRUE' ++ '_maintenance_access' => TRUE, + ] + ); + +@@ -109,6 +128,7 @@ class FlysystemRoutes implements ContainerInjectionInterface { + '/' . $settings['config']['root'] . '/styles/{image_style}/' . $scheme, + [ + '_controller' => 'Drupal\image\Controller\ImageStyleDownloadController::deliver', ++ 'required_derivative_scheme' => $scheme, + '_disable_route_normalizer' => TRUE, + 'scheme' => $scheme, + ], +@@ -116,30 +136,12 @@ class FlysystemRoutes implements ContainerInjectionInterface { + '_access' => 'TRUE', + ], + [ +- '_maintenance_access' => 'TRUE' ++ '_maintenance_access' => TRUE, + ] + ); + } + } + +- if ($this->moduleHandler->moduleExists('image')) { +- // Internal image rotue. +- $routes['flysystem.image_style'] = new Route( +- '/_flysystem/styles/{image_style}/{scheme}', +- [ +- '_controller' => 'Drupal\image\Controller\ImageStyleDownloadController::deliver', +- '_disable_route_normalizer' => TRUE, +- ], +- [ +- '_access' => 'TRUE', +- 'scheme' => '^[a-zA-Z0-9+.-]+$', +- ], +- [ +- '_maintenance_access' => 'TRUE' +- ] +- ); +- } +- + return $routes; + } + diff --git a/src/Flysystem/Azure.php b/src/Flysystem/Azure.php index 4cb6695..9710823 100644 --- a/src/Flysystem/Azure.php +++ b/src/Flysystem/Azure.php @@ -149,15 +149,12 @@ public function getExternalUrl($uri): string { // We use 'responsive_image' module, so each image can generate up to // four derivatives, each taking several seconds. // @see https://helsinkisolutionoffice.atlassian.net/browse/UHF-8204 - if (str_contains($uri, 'styles/') && !file_exists($uri)) { + if (str_contains($uri, '/styles/') && !file_exists($uri)) { // Return a 'local' image style URL until the image is generated and // copied to Azure blob storage. Each derivative is generated when the // image style URL is called for the first time, allowing the generation // to be decoupled from main request. - $localUri = str_replace('azure://', 'public://', $uri); - - return $this->externalUrls[$uri] = UrlHelper::encodePath( - $this->fileUrlGenerator->generateString($localUri)); + return $this->externalUrls[$uri] = $this->getDownloadUrl($uri); } $target = $this->getTarget($uri); diff --git a/tests/src/Kernel/StorageInfoAlterTest.php b/tests/src/Kernel/StorageInfoAlterTest.php index d2aeaf3..15add2e 100644 --- a/tests/src/Kernel/StorageInfoAlterTest.php +++ b/tests/src/Kernel/StorageInfoAlterTest.php @@ -23,7 +23,6 @@ class StorageInfoAlterTest extends FieldKernelTestBase { 'image', 'file', 'flysystem', - 'flysystem_azure', 'helfi_azure_fs', ]; diff --git a/tests/src/Unit/AzureTest.php b/tests/src/Unit/AzureTest.php index 95280d7..d0884ba 100644 --- a/tests/src/Unit/AzureTest.php +++ b/tests/src/Unit/AzureTest.php @@ -7,6 +7,7 @@ use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\File\FileUrlGeneratorInterface; use Drupal\Core\Logger\LoggerChannelFactory; +use Drupal\Core\Routing\UrlGeneratorInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Tests\UnitTestCase; use Drupal\helfi_azure_fs\Flysystem\Azure; @@ -32,18 +33,25 @@ class AzureTest extends UnitTestCase { */ public function testGetExternalUrl() : void { vfsStream::setup('flysystem'); - $fileUrlGenerator = $this->prophesize(FileUrlGeneratorInterface::class); - $fileUrlGenerator->generateString(Argument::any()) - ->shouldBeCalledTimes(2) - ->willReturn( - '/styles/test.jpg', - '/styles/test).jpg', - ); $loggerFactory = new LoggerChannelFactory( $this->prophesize(RequestStack::class)->reveal(), $this->prophesize(AccountInterface::class)->reveal(), ); $loggerFactory->addLogger($this->prophesize(LoggerInterface::class)->reveal()); + $fileUrlGenerator = $this->prophesize(FileUrlGeneratorInterface::class); + $fileUrlGenerator->generateString(Argument::any()); + $url_generator = $this->prophesize(UrlGeneratorInterface::class); + $url_generator + ->generateFromRoute( + 'flysystem.serve', + Argument::any(), + ['absolute' => TRUE], + FALSE + ) + ->shouldBeCalledTimes(1) + ->will(function ($args) { + return 'flysystem.serve: ' . $args[1]['filepath']; + }); $configuration = [ 'protocol' => 'https', @@ -55,19 +63,22 @@ public function testGetExternalUrl() : void { $container = new ContainerBuilder(); $container->set('logger.factory', $loggerFactory); $container->set('file_url_generator', $fileUrlGenerator->reveal()); + $container->set('url_generator', $url_generator->reveal()); + + // Required by Url::fromRoute. + \Drupal::setContainer($container); + $azure = Azure::create($container, $configuration, 'helfi_azure', []); // Make sure non-image style URLs are served directly from blob storage. $this->assertEquals('https://test.blob.core.windows.net/test/test.jpg', $azure->getExternalUrl('vfs://test.jpg')); // Make sure image style URL is passed to file url generator service. - $this->assertEquals('/styles/test.jpg', $azure->getExternalUrl('vfs://styles/test.jpg')); + $this->assertEquals('flysystem.serve: styles/test.jpg', $azure->getExternalUrl('vfs://styles/test.jpg')); + // Test static cache, as generateFromRoute should not be called 2nd time + // and is restricted above to 1 call. + $this->assertEquals('flysystem.serve: styles/test.jpg', $azure->getExternalUrl('vfs://styles/test.jpg')); // Check that file uri is encoded. $this->assertEquals('https://test.blob.core.windows.net/test/test%29.jpg', $azure->getExternalUrl('vfs://test).jpg')); - $this->assertEquals('/styles/test%29.jpg', $azure->getExternalUrl('vfs://styles/test).jpg')); - - // Test static cache, as generateString should not be called 3rd time - // and is restricted above to 2 calls. - $this->assertEquals('/styles/test.jpg', $azure->getExternalUrl('vfs://styles/test.jpg')); } /**