diff options
author | skjnldsv <skjnldsv@protonmail.com> | 2025-01-16 11:46:22 +0100 |
---|---|---|
committer | skjnldsv <skjnldsv@protonmail.com> | 2025-01-16 18:35:40 +0100 |
commit | 7cbcbdc051ac3acaefb0aef731376d87121adf62 (patch) | |
tree | abab8847b4eeeebfe6be3e63dc91808287f0f528 | |
parent | 8998e77c9d35d1c6183ac5d47f8e1ee9cdefa77f (diff) | |
download | nextcloud-server-7cbcbdc051ac3acaefb0aef731376d87121adf62.tar.gz nextcloud-server-7cbcbdc051ac3acaefb0aef731376d87121adf62.zip |
fix(files): conversion api simplification and conflict check
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
-rw-r--r-- | apps/files/lib/Capabilities.php | 10 | ||||
-rw-r--r-- | apps/files/openapi.json | 27 | ||||
-rw-r--r-- | apps/testing/lib/Conversion/ConversionProvider.php | 19 | ||||
-rw-r--r-- | lib/composer/composer/autoload_classmap.php | 2 | ||||
-rw-r--r-- | lib/composer/composer/autoload_psr4.php | 1 | ||||
-rw-r--r-- | lib/composer/composer/autoload_static.php | 10 | ||||
-rw-r--r-- | lib/private/Files/Conversion/ConversionManager.php | 97 | ||||
-rw-r--r-- | lib/public/Files/Conversion/ConversionMimeProvider.php | 66 | ||||
-rw-r--r-- | lib/public/Files/Conversion/ConversionMimeTuple.php | 44 | ||||
-rw-r--r-- | lib/public/Files/Conversion/IConversionManager.php | 4 | ||||
-rw-r--r-- | lib/public/Files/Conversion/IConversionProvider.php | 2 |
11 files changed, 169 insertions, 113 deletions
diff --git a/apps/files/lib/Capabilities.php b/apps/files/lib/Capabilities.php index 88efb4fcaf0..6b50e5807a5 100644 --- a/apps/files/lib/Capabilities.php +++ b/apps/files/lib/Capabilities.php @@ -10,7 +10,7 @@ namespace OCA\Files; use OC\Files\FilenameValidator; use OCA\Files\Service\ChunkedUploadConfig; use OCP\Capabilities\ICapability; -use OCP\Files\Conversion\ConversionMimeTuple; +use OCP\Files\Conversion\ConversionMimeProvider; use OCP\Files\Conversion\IConversionManager; class Capabilities implements ICapability { @@ -24,7 +24,7 @@ class Capabilities implements ICapability { /** * Return this classes capabilities * - * @return array{files: array{'$comment': ?string, bigfilechunking: bool, blacklisted_files: list<mixed>, forbidden_filenames: list<string>, forbidden_filename_basenames: list<string>, forbidden_filename_characters: list<string>, forbidden_filename_extensions: list<string>, chunked_upload: array{max_size: int, max_parallel_count: int}, file_conversions: list<array{from: string, to: list<array{mime: string, name: string}>}>}} + * @return array{files: array{'$comment': ?string, bigfilechunking: bool, blacklisted_files: list<mixed>, forbidden_filenames: list<string>, forbidden_filename_basenames: list<string>, forbidden_filename_characters: list<string>, forbidden_filename_extensions: list<string>, chunked_upload: array{max_size: int, max_parallel_count: int}, file_conversions: list<array{from: string, to: string, extension: string, displayName: string}>}} */ public function getCapabilities(): array { return [ @@ -42,9 +42,9 @@ class Capabilities implements ICapability { 'max_parallel_count' => ChunkedUploadConfig::getMaxParallelCount(), ], - 'file_conversions' => array_map(function (ConversionMimeTuple $mimeTuple) { - return $mimeTuple->jsonSerialize(); - }, $this->fileConversionManager->getMimeTypes()), + 'file_conversions' => array_map(function (ConversionMimeProvider $mimeProvider) { + return $mimeProvider->jsonSerialize(); + }, $this->fileConversionManager->getProviders()), ], ]; } diff --git a/apps/files/openapi.json b/apps/files/openapi.json index 015d4f42648..ddf3535c55f 100644 --- a/apps/files/openapi.json +++ b/apps/files/openapi.json @@ -101,29 +101,22 @@ "type": "object", "required": [ "from", - "to" + "to", + "extension", + "displayName" ], "properties": { "from": { "type": "string" }, "to": { - "type": "array", - "items": { - "type": "object", - "required": [ - "mime", - "name" - ], - "properties": { - "mime": { - "type": "string" - }, - "name": { - "type": "string" - } - } - } + "type": "string" + }, + "extension": { + "type": "string" + }, + "displayName": { + "type": "string" } } } diff --git a/apps/testing/lib/Conversion/ConversionProvider.php b/apps/testing/lib/Conversion/ConversionProvider.php index 15e468b0756..b8d93428694 100644 --- a/apps/testing/lib/Conversion/ConversionProvider.php +++ b/apps/testing/lib/Conversion/ConversionProvider.php @@ -9,7 +9,7 @@ declare(strict_types=1); namespace OCA\Testing\Conversion; -use OCP\Files\Conversion\ConversionMimeTuple; +use OCP\Files\Conversion\ConversionMimeProvider; use OCP\Files\Conversion\IConversionProvider; use OCP\Files\File; use OCP\IL10N; @@ -22,19 +22,26 @@ class ConversionProvider implements IConversionProvider { public function getSupportedMimeTypes(): array { return [ - new ConversionMimeTuple('image/jpeg', [ - ['mime' => 'image/png', 'name' => $this->l10n->t('Image (.png)')], - ]) + new ConversionMimeProvider('image/jpeg', 'image/png', 'png', $this->l10n->t('Image (.png)')), + new ConversionMimeProvider('image/jpeg', 'image/gif', 'gif', $this->l10n->t('Image (.gif)')), ]; } public function convertFile(File $file, string $targetMimeType): mixed { $image = imagecreatefromstring($file->getContent()); - imagepalettetotruecolor($image); + // Start output buffering ob_start(); - imagepng($image); + + // Convert the image to the target format + if ($targetMimeType === 'image/gif') { + imagegif($image); + } else { + imagepng($image); + } + + // End and return the output buffer return ob_get_clean(); } } diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 9916a0eb262..7f328a7bbce 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -377,7 +377,7 @@ return array( 'OCP\\Files\\Config\\IRootMountProvider' => $baseDir . '/lib/public/Files/Config/IRootMountProvider.php', 'OCP\\Files\\Config\\IUserMountCache' => $baseDir . '/lib/public/Files/Config/IUserMountCache.php', 'OCP\\Files\\ConnectionLostException' => $baseDir . '/lib/public/Files/ConnectionLostException.php', - 'OCP\\Files\\Conversion\\ConversionMimeTuple' => $baseDir . '/lib/public/Files/Conversion/ConversionMimeTuple.php', + 'OCP\\Files\\Conversion\\ConversionMimeProvider' => $baseDir . '/lib/public/Files/Conversion/ConversionMimeProvider.php', 'OCP\\Files\\Conversion\\IConversionManager' => $baseDir . '/lib/public/Files/Conversion/IConversionManager.php', 'OCP\\Files\\Conversion\\IConversionProvider' => $baseDir . '/lib/public/Files/Conversion/IConversionProvider.php', 'OCP\\Files\\DavUtil' => $baseDir . '/lib/public/Files/DavUtil.php', diff --git a/lib/composer/composer/autoload_psr4.php b/lib/composer/composer/autoload_psr4.php index b07b2c0074b..09892528d3a 100644 --- a/lib/composer/composer/autoload_psr4.php +++ b/lib/composer/composer/autoload_psr4.php @@ -10,5 +10,6 @@ return array( 'OC\\' => array($baseDir . '/lib/private'), 'OCP\\' => array($baseDir . '/lib/public'), 'NCU\\' => array($baseDir . '/lib/unstable'), + 'Bamarni\\Composer\\Bin\\' => array($vendorDir . '/bamarni/composer-bin-plugin/src'), '' => array($baseDir . '/lib/private/legacy'), ); diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 3c617b14053..559dab76201 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -21,6 +21,10 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 array ( 'NCU\\' => 4, ), + 'B' => + array ( + 'Bamarni\\Composer\\Bin\\' => 21, + ), ); public static $prefixDirsPsr4 = array ( @@ -40,6 +44,10 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 array ( 0 => __DIR__ . '/../../..' . '/lib/unstable', ), + 'Bamarni\\Composer\\Bin\\' => + array ( + 0 => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src', + ), ); public static $fallbackDirsPsr4 = array ( @@ -418,7 +426,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Files\\Config\\IRootMountProvider' => __DIR__ . '/../../..' . '/lib/public/Files/Config/IRootMountProvider.php', 'OCP\\Files\\Config\\IUserMountCache' => __DIR__ . '/../../..' . '/lib/public/Files/Config/IUserMountCache.php', 'OCP\\Files\\ConnectionLostException' => __DIR__ . '/../../..' . '/lib/public/Files/ConnectionLostException.php', - 'OCP\\Files\\Conversion\\ConversionMimeTuple' => __DIR__ . '/../../..' . '/lib/public/Files/Conversion/ConversionMimeTuple.php', + 'OCP\\Files\\Conversion\\ConversionMimeProvider' => __DIR__ . '/../../..' . '/lib/public/Files/Conversion/ConversionMimeProvider.php', 'OCP\\Files\\Conversion\\IConversionManager' => __DIR__ . '/../../..' . '/lib/public/Files/Conversion/IConversionManager.php', 'OCP\\Files\\Conversion\\IConversionProvider' => __DIR__ . '/../../..' . '/lib/public/Files/Conversion/IConversionProvider.php', 'OCP\\Files\\DavUtil' => __DIR__ . '/../../..' . '/lib/public/Files/DavUtil.php', diff --git a/lib/private/Files/Conversion/ConversionManager.php b/lib/private/Files/Conversion/ConversionManager.php index 37fc9a6c754..db49f7afed9 100644 --- a/lib/private/Files/Conversion/ConversionManager.php +++ b/lib/private/Files/Conversion/ConversionManager.php @@ -11,7 +11,7 @@ namespace OC\Files\Conversion; use OC\AppFramework\Bootstrap\Coordinator; use OC\SystemConfig; -use OCP\Files\Conversion\ConversionMimeTuple; +use OCP\Files\Conversion\ConversionMimeProvider; use OCP\Files\Conversion\IConversionManager; use OCP\Files\Conversion\IConversionProvider; use OCP\Files\File; @@ -32,10 +32,10 @@ class ConversionManager implements IConversionManager { 'richdocuments', ]; - /** @var IConversionProvider[] */ + /** @var list<IConversionProvider> */ private array $preferredProviders = []; - /** @var IConversionProvider[] */ + /** @var list<IConversionProvider> */ private array $providers = []; public function __construct( @@ -53,16 +53,28 @@ class ConversionManager implements IConversionManager { return !empty($context->getFileConversionProviders()); } - public function getMimeTypes(): array { - $mimeTypes = []; - - foreach ($this->getProviders() as $provider) { - $mimeTypes[] = $provider->getSupportedMimetypes(); + public function getProviders(): array { + $providers = []; + foreach ($this->getRegisteredProviders() as $provider) { + $providers = array_merge($providers, $provider->getSupportedMimeTypes()); } + return $providers; + } + + /** + * @param string $mime + * @return list<ConversionMimeProvider> + */ + private function getProvidersForMime(string $mime): array { + $mimeTypes = $this->getProviders(); + $filtered = array_filter( + $mimeTypes, + function (ConversionMimeProvider $mimeProvider) use ($mime) { + return $mimeProvider->getFrom() === $mime; + } + ); - /** @var list<ConversionMimeTuple> */ - $mimeTypes = array_merge(...$mimeTypes); - return $mimeTypes; + return array_values($filtered); } public function convert(File $file, string $targetMimeType, ?string $destination = null): string { @@ -80,24 +92,36 @@ class ConversionManager implements IConversionManager { $fileMimeType = $file->getMimetype(); $validProvider = $this->getValidProvider($fileMimeType, $targetMimeType); + $targetExtension = ''; + foreach ($this->getProvidersForMime($fileMimeType) as $mimeProvider) { + if ($mimeProvider->getTo() === $targetMimeType) { + $targetExtension = $mimeProvider->getExtension(); + break; + } + } + if ($validProvider !== null) { $convertedFile = $validProvider->convertFile($file, $targetMimeType); - if ($destination !== null) { - $convertedFile = $this->writeToDestination($destination, $convertedFile); - return $convertedFile->getPath(); + // If destination not provided, we use the same path + // as the original file, but with the new extension + if ($destination === null) { + $basename = pathinfo($file->getPath(), PATHINFO_FILENAME); + $parent = $file->getParent(); + $destination = $parent->getFullPath($basename . '.' . $targetExtension); } - $tmp = $this->tempManager->getTemporaryFile(); - file_put_contents($tmp, $convertedFile); - - return $tmp; + $convertedFile = $this->writeToDestination($destination, $convertedFile); + return $convertedFile->getPath(); } throw new RuntimeException('Could not convert file'); } - public function getProviders(): array { + /** + * @return list<IConversionProvider> + */ + private function getRegisteredProviders(): array { if (count($this->providers) > 0) { return $this->providers; } @@ -121,32 +145,33 @@ class ConversionManager implements IConversionManager { } } - return array_merge([], $this->preferredProviders, $this->providers); + return array_values(array_merge([], $this->preferredProviders, $this->providers)); } private function writeToDestination(string $destination, mixed $content): File { + if ($this->rootFolder->nodeExists($destination)) { + $file = $this->rootFolder->get($destination); + $parent = $file->getParent(); + if (!$parent->isCreatable()) { + throw new GenericFileException('Destination is not creatable'); + } + + $newName = $parent->getNonExistingName(basename($destination)); + $destination = $parent->getFullPath($newName); + } + return $this->rootFolder->newFile($destination, $content); } private function getValidProvider(string $fileMimeType, string $targetMimeType): ?IConversionProvider { - $validProvider = null; - foreach ($this->getProviders() as $provider) { - $suitableMimeTypes = array_filter( - $provider->getSupportedMimeTypes(), - function (ConversionMimeTuple $mimeTuple) use ($fileMimeType, $targetMimeType) { - ['from' => $from, 'to' => $to] = $mimeTuple->jsonSerialize(); - - $supportsTargetMimeType = in_array($targetMimeType, array_column($to, 'mime')); - return ($from === $fileMimeType) && $supportsTargetMimeType; + foreach ($this->getRegisteredProviders() as $provider) { + foreach ($provider->getSupportedMimeTypes() as $mimeProvider) { + if ($mimeProvider->getFrom() === $fileMimeType && $mimeProvider->getTo() === $targetMimeType) { + return $provider; } - ); - - if (!empty($suitableMimeTypes)) { - $validProvider = $provider; - break; } } - - return $validProvider; + + return null; } } diff --git a/lib/public/Files/Conversion/ConversionMimeProvider.php b/lib/public/Files/Conversion/ConversionMimeProvider.php new file mode 100644 index 00000000000..0daf4a10648 --- /dev/null +++ b/lib/public/Files/Conversion/ConversionMimeProvider.php @@ -0,0 +1,66 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCP\Files\Conversion; + +use JsonSerializable; + +/** + * A tuple-like object representing both an original and target + * MIME type for a file conversion + * + * @since 31.0.0 + */ +class ConversionMimeProvider implements JsonSerializable { + /** + * @param string $from The source MIME type of a file + * @param string $to The target MIME type for the file + * @param string $extension The file extension for the target MIME type (e.g. 'png') + * @param string $displayName The human-readable name of the target MIME type (e.g. 'Image (.png)') + * + * @since 31.0.0 + */ + public function __construct( + private string $from, + private string $to, + private string $extension, + private string $displayName, + ) { + } + + public function getFrom(): string { + return $this->from; + } + + public function getTo(): string { + return $this->to; + } + + public function getExtension(): string { + return $this->extension; + } + + public function getDisplayName(): string { + return $this->displayName; + } + + /** + * @return array{from: string, to: string, extension: string, displayName: string} + * + * @since 31.0.0 + */ + public function jsonSerialize(): array { + return [ + 'from' => $this->from, + 'to' => $this->to, + 'extension' => $this->extension, + 'displayName' => $this->displayName, + ]; + } +} diff --git a/lib/public/Files/Conversion/ConversionMimeTuple.php b/lib/public/Files/Conversion/ConversionMimeTuple.php deleted file mode 100644 index 0180f3311f3..00000000000 --- a/lib/public/Files/Conversion/ConversionMimeTuple.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -declare(strict_types=1); - -/** - * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -namespace OCP\Files\Conversion; - -use JsonSerializable; - -/** - * A tuple-like object representing both an original and target - * MIME type for a file conversion - * - * @since 31.0.0 - */ -class ConversionMimeTuple implements JsonSerializable { - /** - * @param string $from The original MIME type of a file - * @param list<array{mime: string, name: string}> $to The desired MIME type for the file mapped to its translated name - * - * @since 31.0.0 - */ - public function __construct( - private string $from, - private array $to, - ) { - } - - /** - * @return array{from: string, to: list<array{mime: string, name: string}>} - * - * @since 31.0.0 - */ - public function jsonSerialize(): array { - return [ - 'from' => $this->from, - 'to' => $this->to, - ]; - } -} diff --git a/lib/public/Files/Conversion/IConversionManager.php b/lib/public/Files/Conversion/IConversionManager.php index 59ff580fdf1..ed418129d3b 100644 --- a/lib/public/Files/Conversion/IConversionManager.php +++ b/lib/public/Files/Conversion/IConversionManager.php @@ -25,11 +25,11 @@ interface IConversionManager { /** * Gets all supported MIME type conversions * - * @return list<ConversionMimeTuple> + * @return list<ConversionMimeProvider> * * @since 31.0.0 */ - public function getMimeTypes(): array; + public function getProviders(): array; /** * Convert a file to a given MIME type diff --git a/lib/public/Files/Conversion/IConversionProvider.php b/lib/public/Files/Conversion/IConversionProvider.php index b0a09fc93a4..3b5c5945c99 100644 --- a/lib/public/Files/Conversion/IConversionProvider.php +++ b/lib/public/Files/Conversion/IConversionProvider.php @@ -21,7 +21,7 @@ interface IConversionProvider { /** * Get an array of MIME type tuples this conversion provider supports * - * @return list<ConversionMimeTuple> + * @return list<ConversionMimeProvider> * * @since 31.0.0 */ |