From ce983e5acb1983849183014593102a7783ca3d5e Mon Sep 17 00:00:00 2001 From: Karsten Dambekalns Date: Tue, 10 Sep 2024 11:42:16 +0200 Subject: [PATCH 1/9] Raise minimum requirements to Flow 7.3 & PHP 8.0 --- composer.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index b497eb8..fd652a7 100644 --- a/composer.json +++ b/composer.json @@ -4,8 +4,9 @@ "name": "flownative/neos-pixxio", "license": "MIT", "require": { - "neos/flow": "^6.3 || ^7.0 || ^8.0 || dev-master", - "neos/media": "^5.3 || ^7.0 || ^8.0 || dev-master", + "php": "^8.0", + "neos/flow": "^7.3 || ^8.0", + "neos/media": "^7.3 || ^8.0", "guzzlehttp/guzzle": "^6.0 || ^7.0", "behat/transliterator": "~1.0" }, From 8da4afe951d66ee6ce2d7fd273357f69354f3261 Mon Sep 17 00:00:00 2001 From: Karsten Dambekalns Date: Tue, 10 Sep 2024 11:40:30 +0200 Subject: [PATCH 2/9] Code cleanup --- Classes/AssetSource/PixxioAssetProxy.php | 16 ++++---- Classes/AssetSource/PixxioAssetProxyQuery.php | 7 ++-- .../PixxioAssetProxyQueryResult.php | 3 +- .../PixxioAssetProxyRepository.php | 15 ++++--- Classes/Command/PixxioCommandController.php | 22 +++++----- Classes/Controller/PixxioController.php | 6 +-- Classes/Domain/Model/ClientSecret.php | 4 +- Classes/Service/PixxioClient.php | 40 +++++++++---------- Classes/Service/PixxioServiceFactory.php | 3 +- 9 files changed, 58 insertions(+), 58 deletions(-) diff --git a/Classes/AssetSource/PixxioAssetProxy.php b/Classes/AssetSource/PixxioAssetProxy.php index 66c19f3..e491504 100644 --- a/Classes/AssetSource/PixxioAssetProxy.php +++ b/Classes/AssetSource/PixxioAssetProxy.php @@ -91,12 +91,12 @@ final class PixxioAssetProxy implements AssetProxyInterface, HasRemoteOriginalIn private $originalUri; /** - * @var int + * @var int|null */ private $widthInPixels; /** - * @var int + * @var int|null */ private $heightInPixels; @@ -120,7 +120,7 @@ final class PixxioAssetProxy implements AssetProxyInterface, HasRemoteOriginalIn public static function fromJsonObject(stdClass $jsonObject, PixxioAssetSource $assetSource): PixxioAssetProxy { $assetSourceOptions = $assetSource->getAssetSourceOptions(); - $pixxioOriginalMediaType = MediaTypes::getMediaTypeFromFilename('foo.' . strtolower($jsonObject->fileType));; + $pixxioOriginalMediaType = MediaTypes::getMediaTypeFromFilename('foo.' . strtolower($jsonObject->fileType)); $usePixxioThumbnailAsOriginal = (!isset($assetSourceOptions['mediaTypes'][$pixxioOriginalMediaType]) || $assetSourceOptions['mediaTypes'][$pixxioOriginalMediaType]['usePixxioThumbnailAsOriginal'] === false); $modifiedFileType = $usePixxioThumbnailAsOriginal ? 'jpg' : strtolower($jsonObject->fileType); @@ -270,7 +270,7 @@ public function getHeightInPixels(): ?int } /** - * @return UriInterface + * @return UriInterface|null */ public function getThumbnailUri(): ?UriInterface { @@ -278,7 +278,7 @@ public function getThumbnailUri(): ?UriInterface } /** - * @return UriInterface + * @return UriInterface|null */ public function getPreviewUri(): ?UriInterface { @@ -299,13 +299,13 @@ public function getImportStream() } return false; - } catch (GuzzleException $e) { - throw new ConnectionException('Retrieving file failed: ' . $e->getMessage(), 1542808207, $e); + } catch (GuzzleException $exception) { + throw new ConnectionException('Retrieving file failed: ' . $exception->getMessage(), 1542808207, $exception); } } /** - * @return string + * @return string|null */ public function getLocalAssetIdentifier(): ?string { diff --git a/Classes/AssetSource/PixxioAssetProxyQuery.php b/Classes/AssetSource/PixxioAssetProxyQuery.php index 2823c1b..6d98ea6 100644 --- a/Classes/AssetSource/PixxioAssetProxyQuery.php +++ b/Classes/AssetSource/PixxioAssetProxyQuery.php @@ -17,6 +17,7 @@ use Flownative\Pixxio\Exception\ConnectionException; use Flownative\Pixxio\Exception\MissingClientSecretException; use GuzzleHttp\Psr7\Response; +use GuzzleHttp\Utils; use Neos\Flow\Annotations\Inject; use Neos\Flow\Log\ThrowableStorageInterface; use Neos\Flow\Log\Utility\LogEnvironment; @@ -216,7 +217,7 @@ public function count(): int { try { $response = $this->sendSearchRequest(1, []); - $responseObject = \GuzzleHttp\json_decode($response->getBody()); + $responseObject = Utils::jsonDecode($response->getBody()->getContents()); if (!isset($responseObject->quantity)) { if (isset($responseObject->help)) { @@ -225,7 +226,7 @@ public function count(): int } return 0; } - return $responseObject->quantity; + return (int)$responseObject->quantity; } catch (AuthenticationFailedException $exception) { $message = $this->throwableStorage->logThrowable(new ConnectionException('Connection to pixx.io failed.', 1526629541, $exception)); $this->logger->error($message, LogEnvironment::fromMethodName(__METHOD__)); @@ -250,7 +251,7 @@ public function getArrayResult(): array try { $assetProxies = []; $response = $this->sendSearchRequest($this->limit, $this->orderings); - $responseObject = \GuzzleHttp\json_decode($response->getBody()); + $responseObject = Utils::jsonDecode($response->getBody()->getContents()); if (!isset($responseObject->files)) { return []; diff --git a/Classes/AssetSource/PixxioAssetProxyQueryResult.php b/Classes/AssetSource/PixxioAssetProxyQueryResult.php index 946d876..782a58f 100644 --- a/Classes/AssetSource/PixxioAssetProxyQueryResult.php +++ b/Classes/AssetSource/PixxioAssetProxyQueryResult.php @@ -13,6 +13,7 @@ * source code. */ +use Flownative\Pixxio\Exception\ConnectionException; use Neos\Media\Domain\Model\AssetSource\AssetProxy\AssetProxyInterface; use Neos\Media\Domain\Model\AssetSource\AssetProxyQueryInterface; use Neos\Media\Domain\Model\AssetSource\AssetProxyQueryResultInterface; @@ -141,7 +142,7 @@ public function offsetUnset($offset) /** * @return int - * @throws \Flownative\Pixxio\Exception\ConnectionException + * @throws ConnectionException */ public function count(): int { diff --git a/Classes/AssetSource/PixxioAssetProxyRepository.php b/Classes/AssetSource/PixxioAssetProxyRepository.php index ab2bb1c..9b1f790 100644 --- a/Classes/AssetSource/PixxioAssetProxyRepository.php +++ b/Classes/AssetSource/PixxioAssetProxyRepository.php @@ -19,6 +19,7 @@ use Flownative\Pixxio\Exception\AuthenticationFailedException; use Flownative\Pixxio\Exception\ConnectionException; use Flownative\Pixxio\Exception\MissingClientSecretException; +use GuzzleHttp\Utils; use Neos\Cache\Frontend\StringFrontend; use Neos\Flow\ObjectManagement\DependencyInjection\DependencyProxy; use Neos\Media\Domain\Model\AssetCollection; @@ -90,10 +91,10 @@ public function getAssetProxy(string $identifier): AssetProxyInterface $cacheEntry = $this->assetProxyCache->get($cacheEntryIdentifier); if ($cacheEntry) { - $responseObject = \GuzzleHttp\json_decode($cacheEntry); + $responseObject = Utils::jsonDecode($cacheEntry); } else { $response = $client->getFile($identifier); - $responseObject = \GuzzleHttp\json_decode($response->getBody()); + $responseObject = Utils::jsonDecode($response->getBody()->getContents()); if (!$responseObject instanceof \stdClass) { throw new AssetNotFoundException('Asset not found', 1526636260); @@ -102,19 +103,18 @@ public function getAssetProxy(string $identifier): AssetProxyInterface switch ($responseObject->status) { case 403: throw new AccessToAssetDeniedException(sprintf('Failed retrieving asset: %s', $response->help ?? '-') , 1589815740); - break; default: throw new AssetNotFoundException(sprintf('Failed retrieving asset, unexpected API response: %s', $response->help ?? '-') , 1589354288); } } - $this->assetProxyCache->set($cacheEntryIdentifier, \GuzzleHttp\json_encode($responseObject, JSON_FORCE_OBJECT)); + $this->assetProxyCache->set($cacheEntryIdentifier, Utils::jsonEncode($responseObject, JSON_FORCE_OBJECT)); } return PixxioAssetProxy::fromJsonObject($responseObject, $this->assetSource); } /** - * @param AssetTypeFilter $assetType + * @param AssetTypeFilter|null $assetType */ public function filterByType(AssetTypeFilter $assetType = null): void { @@ -123,7 +123,7 @@ public function filterByType(AssetTypeFilter $assetType = null): void public function filterByCollection(AssetCollection $assetCollection = null): void { - $this->assetCollectionFilter = $assetCollection ? $assetCollection->getTitle() : null; + $this->assetCollectionFilter = $assetCollection?->getTitle(); } /** @@ -183,8 +183,7 @@ public function findUntagged(): AssetProxyQueryResultInterface */ public function countAll(): int { - $query = new PixxioAssetProxyQuery($this->assetSource); - return $query->count(); + return (new PixxioAssetProxyQuery($this->assetSource))->count(); } /** diff --git a/Classes/Command/PixxioCommandController.php b/Classes/Command/PixxioCommandController.php index d059af3..5c046e8 100644 --- a/Classes/Command/PixxioCommandController.php +++ b/Classes/Command/PixxioCommandController.php @@ -6,10 +6,11 @@ use Flownative\Pixxio\AssetSource\PixxioAssetSource; use Flownative\Pixxio\Exception\AccessToAssetDeniedException; use Flownative\Pixxio\Exception\AuthenticationFailedException; +use Flownative\Pixxio\Exception\ConnectionException; use Flownative\Pixxio\Exception\MissingClientSecretException; +use GuzzleHttp\Utils; use Neos\Flow\Annotations as Flow; use Neos\Flow\Cli\CommandController; -use Neos\Flow\Cli\Exception\StopCommandException; use Neos\Flow\Persistence\Exception\IllegalObjectTypeException; use Neos\Media\Domain\Model\Asset; use Neos\Media\Domain\Model\AssetCollection; @@ -75,11 +76,11 @@ public function tagUsedAssetsCommand(string $assetSource = 'flownative-pixxio', try { $pixxioAssetSource = new PixxioAssetSource($assetSourceIdentifier, $this->assetSourcesConfiguration[$assetSourceIdentifier]['assetSourceOptions']); $pixxioClient = $pixxioAssetSource->getPixxioClient(); - } catch (MissingClientSecretException $e) { + } catch (MissingClientSecretException) { $this->outputLine('Authentication error: Missing client secret'); exit(1); - } catch (AuthenticationFailedException $e) { - $this->outputLine('Authentication error: %s', [$e->getMessage()]); + } catch (AuthenticationFailedException $exception) { + $this->outputLine('Authentication error: %s', [$exception->getMessage()]); exit(1); } @@ -148,6 +149,7 @@ public function tagUsedAssetsCommand(string $assetSource = 'flownative-pixxio', * * @param string $assetSource * @param bool $quiet + * @throws IllegalObjectTypeException */ public function updateMetadataCommand(string $assetSource = 'flownative-pixxio', bool $quiet = false): void { @@ -233,7 +235,7 @@ public function updateMetadataCommand(string $assetSource = 'flownative-pixxio', * @param bool $dryRun If set, no changes will be made. * @return void * @throws IllegalObjectTypeException - * @throws \Flownative\Pixxio\Exception\ConnectionException + * @throws ConnectionException */ public function importCategoriesAsCollectionsCommand(string $assetSourceIdentifier = 'flownative-pixxio', bool $quiet = true, bool $dryRun = false): void { @@ -242,26 +244,26 @@ public function importCategoriesAsCollectionsCommand(string $assetSourceIdentifi try { $pixxioAssetSource = new PixxioAssetSource($assetSourceIdentifier, $this->assetSourcesConfiguration[$assetSourceIdentifier]['assetSourceOptions']); $cantoClient = $pixxioAssetSource->getPixxioClient(); - } catch (\Exception $e) { + } catch (\Exception) { $this->outputLine('pixx.io client could not be created'); $this->quit(1); } $response = $cantoClient->getCategories(); - $responseObject = \GuzzleHttp\json_decode($response->getBody()); + $responseObject = Utils::jsonDecode($response->getBody()->getContents()); foreach ($responseObject->categories as $categoryPath) { $categoryPath = ltrim($categoryPath, '/'); if ($this->shouldBeImportedAsAssetCollection($categoryPath)) { $assetCollection = $this->assetCollectionRepository->findOneByTitle($categoryPath); - if (!($assetCollection instanceof AssetCollection)) { + if ($assetCollection instanceof AssetCollection) { + !$quiet && $this->outputLine('= %s', [$categoryPath]); + } else { if (!$dryRun) { $assetCollection = new AssetCollection($categoryPath); $this->assetCollectionRepository->add($assetCollection); } !$quiet && $this->outputLine('+ %s', [$categoryPath]); - } else { - !$quiet && $this->outputLine('= %s', [$categoryPath]); } } else { !$quiet && $this->outputLine('o %s', [$categoryPath]); diff --git a/Classes/Controller/PixxioController.php b/Classes/Controller/PixxioController.php index 21550fc..1a9fb46 100644 --- a/Classes/Controller/PixxioController.php +++ b/Classes/Controller/PixxioController.php @@ -19,7 +19,6 @@ use Flownative\Pixxio\Exception\AuthenticationFailedException; use Flownative\Pixxio\Exception\MissingClientSecretException; use Neos\Flow\Annotations as Flow; -use Neos\Flow\Mvc\Exception\StopActionException; use Neos\Flow\Mvc\Exception\UnsupportedRequestTypeException; use Neos\Flow\Persistence\Exception\IllegalObjectTypeException; use Neos\Flow\Security\Context; @@ -70,10 +69,9 @@ public function indexAction() } /** - * @param string $refreshToken - * @throws StopActionException - * @throws UnsupportedRequestTypeException + * @param ?string|null $refreshToken * @throws IllegalObjectTypeException + * @throws UnsupportedRequestTypeException */ public function updateRefreshTokenAction(string $refreshToken = null) { diff --git a/Classes/Domain/Model/ClientSecret.php b/Classes/Domain/Model/ClientSecret.php index 08fba6d..76a43ad 100644 --- a/Classes/Domain/Model/ClientSecret.php +++ b/Classes/Domain/Model/ClientSecret.php @@ -36,7 +36,7 @@ class ClientSecret /** * @ORM\Column(nullable=true, type="text") - * @var string + * @var string|null */ protected $accessToken; @@ -73,7 +73,7 @@ public function setRefreshToken(string $refreshToken): void } /** - * @return string + * @return string|null */ public function getAccessToken(): ?string { diff --git a/Classes/Service/PixxioClient.php b/Classes/Service/PixxioClient.php index 86a8532..b4f9601 100644 --- a/Classes/Service/PixxioClient.php +++ b/Classes/Service/PixxioClient.php @@ -19,6 +19,7 @@ use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Psr7\Uri; +use GuzzleHttp\Utils; use Neos\Media\Domain\Model\AssetSource\SupportsSortingInterface; use Psr\Http\Message\ResponseInterface; @@ -60,7 +61,7 @@ final class PixxioClient /** * @var array */ - private $fields = [ + private static array $fields = [ 'id', 'originalFilename', 'fileType', 'keywords', 'createDate', 'imageHeight', 'imageWidth', 'originalPath', 'subject', 'description', 'modifyDate', 'fileSize', 'modifiedImagePaths', 'imagePath', 'dynamicMetadata' ]; @@ -108,9 +109,8 @@ private function configureImageOptionsWithFallback(array $imageOptions): void continue; } $imageOption = $imageOptions[$imageOptionPresetKey]; - if (isset($imageOption['crop']) && $imageOption['crop'] === false && isset($imageOption['height'])) { - unset($imageOption['height']); - unset($imageOption['crop']); + if (isset($imageOption['crop'], $imageOption['height']) && $imageOption['crop'] === false) { + unset($imageOption['height'], $imageOption['crop']); } $this->imageOptions[] = $imageOption; } @@ -133,11 +133,11 @@ public function authenticate(string $refreshToken): void ] ] ); - } catch (GuzzleException $e) { - throw new AuthenticationFailedException('Authentication failed: ' . $e->getMessage(), 1542808119); + } catch (GuzzleException $exception) { + throw new AuthenticationFailedException('Authentication failed: ' . $exception->getMessage(), 1542808119); } - $result = \GuzzleHttp\json_decode($response->getBody()->getContents()); + $result = Utils::jsonDecode($response->getBody()->getContents()); if ($result->success === 'true' && isset($result->accessToken)) { $this->accessToken = $result->accessToken; return; @@ -155,19 +155,19 @@ public function getFile(string $id): ResponseInterface { $options = new \stdClass(); $options->imageOptions = $this->imageOptions; - $options->fields = $this->fields; + $options->fields = static::$fields; $uri = new Uri( $this->apiEndpointUri . '/json/files/' . $id); $uri = $uri->withQuery( 'accessToken=' . $this->accessToken . '&' . - 'options=' . \GuzzleHttp\json_encode($options) + 'options=' . Utils::jsonEncode($options) ); $client = new Client($this->apiClientOptions); try { return $client->request('GET', $uri); - } catch (GuzzleException $e) { - throw new ConnectionException('Retrieving file failed: ' . $e->getMessage(), 1542808207); + } catch (GuzzleException $exception) { + throw new ConnectionException('Retrieving file failed: ' . $exception->getMessage(), 1542808207); } } @@ -198,12 +198,12 @@ public function updateFile(string $id, array $metadata): ResponseInterface $uri, [ 'form_params' => [ - 'options' => \GuzzleHttp\json_encode($options) + 'options' => Utils::jsonEncode($options) ] ] ); - } catch (GuzzleException $e) { - throw new ConnectionException('Updating file failed: ' . $e->getMessage(), 1587559150); + } catch (GuzzleException $exception) { + throw new ConnectionException('Updating file failed: ' . $exception->getMessage(), 1587559150); } } @@ -223,7 +223,7 @@ public function search(string $queryExpression, array $formatTypes, array $fileT $options = new \stdClass(); $options->pagination = $limit . '-' . (int)($offset / $limit + 1); $options->imageOptions = $this->imageOptions; - $options->fields = $this->fields; + $options->fields = static::$fields; $options->formatType = $formatTypes; $options->fileType = implode(',', $fileTypes); @@ -248,14 +248,14 @@ public function search(string $queryExpression, array $formatTypes, array $fileT $uri = new Uri( $this->apiEndpointUri . '/json/files'); $uri = $uri->withQuery( 'accessToken=' . $this->accessToken . '&' . - 'options=' . \GuzzleHttp\json_encode($options) + 'options=' . Utils::jsonEncode($options) ); $client = new Client($this->apiClientOptions); try { return $client->request('GET', $uri); - } catch (GuzzleException $e) { - throw new ConnectionException('Search failed: ' . $e->getMessage(), 1542808181); + } catch (GuzzleException $exception) { + throw new ConnectionException('Search failed: ' . $exception->getMessage(), 1542808181); } } @@ -273,8 +273,8 @@ public function getCategories(): ResponseInterface $client = new Client($this->apiClientOptions); try { return $client->request('GET', $uri); - } catch (GuzzleException $e) { - throw new ConnectionException('Retrieving categories failed: ' . $e->getMessage(), 1642430939); + } catch (GuzzleException $exception) { + throw new ConnectionException('Retrieving categories failed: ' . $exception->getMessage(), 1642430939); } } diff --git a/Classes/Service/PixxioServiceFactory.php b/Classes/Service/PixxioServiceFactory.php index 5f81bb0..85bd5e3 100644 --- a/Classes/Service/PixxioServiceFactory.php +++ b/Classes/Service/PixxioServiceFactory.php @@ -48,12 +48,11 @@ class PixxioServiceFactory */ public function createForAccount(string $accountIdentifier, string $apiEndpointUri, string $apiKey, array $apiClientOptions, array $imageOptions) { - $client = new PixxioClient( + return new PixxioClient( $apiEndpointUri, $apiKey, $apiClientOptions, $imageOptions ); - return $client; } } From 833d4c7f12cdceaccd38fa88f26ee38fb1eead8a Mon Sep 17 00:00:00 2001 From: Karsten Dambekalns Date: Tue, 10 Sep 2024 11:40:54 +0200 Subject: [PATCH 3/9] Drop unused method parameter --- Classes/AssetSource/PixxioAssetSource.php | 1 - Classes/Service/PixxioServiceFactory.php | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Classes/AssetSource/PixxioAssetSource.php b/Classes/AssetSource/PixxioAssetSource.php index 068d0cd..ab2e65a 100644 --- a/Classes/AssetSource/PixxioAssetSource.php +++ b/Classes/AssetSource/PixxioAssetSource.php @@ -300,7 +300,6 @@ public function getPixxioClient(): PixxioClient } $this->pixxioClient = $this->pixxioServiceFactory->createForAccount( - $account->getAccountIdentifier(), $this->apiEndpointUri, $this->apiKey, $this->apiClientOptions, diff --git a/Classes/Service/PixxioServiceFactory.php b/Classes/Service/PixxioServiceFactory.php index 85bd5e3..2b9af38 100644 --- a/Classes/Service/PixxioServiceFactory.php +++ b/Classes/Service/PixxioServiceFactory.php @@ -39,14 +39,13 @@ class PixxioServiceFactory /** * Creates a new PixxioClient instance and authenticates against the Pixx.io API * - * @param string $accountIdentifier * @param string $apiEndpointUri * @param string $apiKey * @param array $apiClientOptions * @param array $imageOptions * @return PixxioClient */ - public function createForAccount(string $accountIdentifier, string $apiEndpointUri, string $apiKey, array $apiClientOptions, array $imageOptions) + public function createForAccount(string $apiEndpointUri, string $apiKey, array $apiClientOptions, array $imageOptions): PixxioClient { return new PixxioClient( $apiEndpointUri, From f44e8bb49b6470eeaf11e2e993cb232bfe9563ef Mon Sep 17 00:00:00 2001 From: Karsten Dambekalns Date: Tue, 10 Sep 2024 11:42:55 +0200 Subject: [PATCH 4/9] Use strict typing --- Classes/AssetSource/PixxioAssetProxy.php | 134 +++--------------- Classes/AssetSource/PixxioAssetProxyQuery.php | 97 ++----------- .../PixxioAssetProxyQueryResult.php | 47 ++---- .../PixxioAssetProxyRepository.php | 70 ++------- Classes/AssetSource/PixxioAssetSource.php | 109 +++----------- Classes/Command/PixxioCommandController.php | 9 +- Classes/Controller/PixxioController.php | 12 +- Classes/Domain/Model/ClientSecret.php | 27 +--- .../Repository/ClientSecretRepository.php | 5 +- .../AccessToAssetDeniedException.php | 1 + Classes/Exception/AssetNotFoundException.php | 1 + .../AuthenticationFailedException.php | 1 + Classes/Exception/ConnectionException.php | 1 + Classes/Exception/Exception.php | 1 + .../MissingClientSecretException.php | 1 + Classes/Service/PixxioClient.php | 15 +- Classes/Service/PixxioServiceFactory.php | 1 + 17 files changed, 95 insertions(+), 437 deletions(-) diff --git a/Classes/AssetSource/PixxioAssetProxy.php b/Classes/AssetSource/PixxioAssetProxy.php index e491504..238e28e 100644 --- a/Classes/AssetSource/PixxioAssetProxy.php +++ b/Classes/AssetSource/PixxioAssetProxy.php @@ -1,4 +1,5 @@ label = $jsonObject->subject; $assetProxy->filename = Transliterator::urlize($jsonObject->subject) . '.' . $modifiedFileType; $assetProxy->lastModified = new \DateTime($jsonObject->modifyDate ?? '1.1.2000'); - $assetProxy->fileSize = $jsonObject->fileSize; + $assetProxy->fileSize = (int)$jsonObject->fileSize; $assetProxy->mediaType = MediaTypes::getMediaTypeFromFilename('foo.' . $modifiedFileType); $assetProxy->tags = isset($jsonObject->keywords) ? explode(',', $jsonObject->keywords) : []; @@ -138,8 +94,8 @@ public static function fromJsonObject(stdClass $jsonObject, PixxioAssetSource $a $assetProxy->iptcProperties['CaptionAbstract'] = $jsonObject->description ?? ''; $assetProxy->iptcProperties['CopyrightNotice'] = $jsonObject->dynamicMetadata->CopyrightNotice ?? ''; - $assetProxy->widthInPixels = $jsonObject->imageWidth ?? null; - $assetProxy->heightInPixels = $jsonObject->imageHeight ?? null; + $assetProxy->widthInPixels = $jsonObject->imageWidth ? (int)$jsonObject->imageWidth : null; + $assetProxy->heightInPixels = $jsonObject->imageHeight ? (int)$jsonObject->imageHeight : null; if (isset($jsonObject->modifiedImagePaths)) { $modifiedImagePaths = $jsonObject->modifiedImagePaths; @@ -171,122 +127,77 @@ public static function fromJsonObject(stdClass $jsonObject, PixxioAssetSource $a return $assetProxy; } - /** - * @return AssetSourceInterface - */ public function getAssetSource(): AssetSourceInterface { return $this->assetSource; } - /** - * @return string - */ public function getIdentifier(): string { return $this->identifier; } - /** - * @return string - */ public function getLabel(): string { return $this->label; } - /** - * @return string - */ public function getFilename(): string { return $this->filename; } - /** - * @return \DateTimeInterface - */ public function getLastModified(): \DateTimeInterface { return $this->lastModified; } - /** - * @return int - */ public function getFileSize(): int { return $this->fileSize; } - /** - * @return string - */ public function getMediaType(): string { return $this->mediaType; } - /** - * @param string $propertyName - * @return bool - */ public function hasIptcProperty(string $propertyName): bool { return isset($this->iptcProperties[$propertyName]); } - /** - * @param string $propertyName - * @return string - */ public function getIptcProperty(string $propertyName): string { return $this->iptcProperties[$propertyName] ?? ''; } - /** - * @return array - */ public function getIptcProperties(): array { return $this->iptcProperties; } - /** - * @return int|null - */ public function getWidthInPixels(): ?int { return $this->widthInPixels; } - /** - * @return int|null - */ public function getHeightInPixels(): ?int { return $this->heightInPixels; } - /** - * @return UriInterface|null - */ public function getThumbnailUri(): ?UriInterface { return $this->thumbnailUri; } - /** - * @return UriInterface|null - */ public function getPreviewUri(): ?UriInterface { return $this->previewUri; } /** - * @return bool|resource * @throws ConnectionException */ public function getImportStream() @@ -304,26 +215,17 @@ public function getImportStream() } } - /** - * @return string|null - */ public function getLocalAssetIdentifier(): ?string { $importedAsset = $this->importedAssetRepository->findOneByAssetSourceIdentifierAndRemoteAssetIdentifier($this->assetSource->getIdentifier(), $this->identifier); return ($importedAsset instanceof ImportedAsset ? $importedAsset->getLocalAssetIdentifier() : null); } - /** - * @return array - */ public function getTags(): array { return $this->tags; } - /** - * @return bool - */ public function isImported(): bool { return true; diff --git a/Classes/AssetSource/PixxioAssetProxyQuery.php b/Classes/AssetSource/PixxioAssetProxyQuery.php index 6d98ea6..1f3ac0a 100644 --- a/Classes/AssetSource/PixxioAssetProxyQuery.php +++ b/Classes/AssetSource/PixxioAssetProxyQuery.php @@ -1,4 +1,5 @@ assetSource = $assetSource; } - /** - * @param int $offset - */ public function setOffset(int $offset): void { $this->offset = $offset; } - /** - * @return int - */ public function getOffset(): int { return $this->offset; } - /** - * @param int $limit - */ public function setLimit(int $limit): void { $this->limit = $limit; } - /** - * @return int - */ public function getLimit(): int { return $this->limit; } - /** - * @param string $searchTerm - */ public function setSearchTerm(string $searchTerm): void { $this->searchTerm = $searchTerm; } - /** - * @return string - */ public function getSearchTerm(): string { return $this->searchTerm; } - /** - * @param string $assetTypeFilter - */ public function setAssetTypeFilter(string $assetTypeFilter): void { $this->assetTypeFilter = $assetTypeFilter; } - /** - * @return string - */ public function getAssetTypeFilter(): string { return $this->assetTypeFilter; } - /** - * @param ?string $assetCollectionFilter - */ public function setAssetCollectionFilter(?string $assetCollectionFilter): void { $this->assetCollectionFilter = $assetCollectionFilter; } - /** - * @return ?string - */ public function getAssetCollectionFilter(): ?string { return $this->assetCollectionFilter; } - /** - * @return array - */ public function getOrderings(): array { return $this->orderings; } - /** - * @param array $orderings - */ public function setOrderings(array $orderings): void { $this->orderings = $orderings; } - /** - * @return string - */ public function getParentFolderIdentifier(): string { return $this->parentFolderIdentifier; } - /** - * @param string $parentFolderIdentifier - */ public function setParentFolderIdentifier(string $parentFolderIdentifier): void { $this->parentFolderIdentifier = $parentFolderIdentifier; } - /** - * @return AssetProxyQueryResultInterface - */ public function execute(): AssetProxyQueryResultInterface { return new PixxioAssetProxyQueryResult($this); } - /** - * @return int - */ public function count(): int { try { @@ -272,9 +198,6 @@ public function getArrayResult(): array } /** - * @param int $limit - * @param array $orderings - * @return Response * @throws AuthenticationFailedException * @throws MissingClientSecretException * @throws ConnectionException diff --git a/Classes/AssetSource/PixxioAssetProxyQueryResult.php b/Classes/AssetSource/PixxioAssetProxyQueryResult.php index 782a58f..44e02c8 100644 --- a/Classes/AssetSource/PixxioAssetProxyQueryResult.php +++ b/Classes/AssetSource/PixxioAssetProxyQueryResult.php @@ -1,4 +1,5 @@ query = $query; } - /** - * @return void - */ private function initialize(): void { if ($this->assetProxies === null) { @@ -62,17 +45,11 @@ private function initialize(): void } } - /** - * @return AssetProxyQueryInterface - */ public function getQuery(): AssetProxyQueryInterface { return clone $this->query; } - /** - * @return AssetProxyInterface|null - */ public function getFirst(): ?AssetProxyInterface { $this->initialize(); @@ -94,7 +71,7 @@ public function current() return $this->assetProxiesIterator->current(); } - public function next() + public function next(): void { $this->initialize(); $this->assetProxiesIterator->next(); @@ -106,19 +83,19 @@ public function key() return $this->assetProxiesIterator->key(); } - public function valid() + public function valid(): bool { $this->initialize(); return $this->assetProxiesIterator->valid(); } - public function rewind() + public function rewind(): void { $this->initialize(); $this->assetProxiesIterator->rewind(); } - public function offsetExists($offset) + public function offsetExists($offset): bool { $this->initialize(); return $this->assetProxiesIterator->offsetExists($offset); @@ -130,7 +107,7 @@ public function offsetGet($offset) return $this->assetProxiesIterator->offsetGet($offset); } - public function offsetSet($offset, $value) + public function offsetSet($offset, $value): void { $this->initialize(); $this->assetProxiesIterator->offsetSet($offset, $value); @@ -140,10 +117,6 @@ public function offsetUnset($offset) { } - /** - * @return int - * @throws ConnectionException - */ public function count(): int { if ($this->numberOfAssetProxies === null) { diff --git a/Classes/AssetSource/PixxioAssetProxyRepository.php b/Classes/AssetSource/PixxioAssetProxyRepository.php index 9b1f790..a1b883a 100644 --- a/Classes/AssetSource/PixxioAssetProxyRepository.php +++ b/Classes/AssetSource/PixxioAssetProxyRepository.php @@ -1,4 +1,5 @@ assetSource = $assetSource; } /** - * @param string $identifier - * @return AssetProxyInterface * @throws AssetNotFoundExceptionInterface * @throws AssetSourceConnectionExceptionInterface * @throws MissingClientSecretException * @throws AuthenticationFailedException * @throws AssetNotFoundException * @throws ConnectionException - * @throws \Neos\Cache\Exception - * @throws Exception + * @throws CacheException */ public function getAssetProxy(string $identifier): AssetProxyInterface { @@ -113,9 +93,6 @@ public function getAssetProxy(string $identifier): AssetProxyInterface return PixxioAssetProxy::fromJsonObject($responseObject, $this->assetSource); } - /** - * @param AssetTypeFilter|null $assetType - */ public function filterByType(AssetTypeFilter $assetType = null): void { $this->assetTypeFilter = (string)$assetType ?: 'All'; @@ -126,9 +103,6 @@ public function filterByCollection(AssetCollection $assetCollection = null): voi $this->assetCollectionFilter = $assetCollection?->getTitle(); } - /** - * @return AssetProxyQueryResultInterface - */ public function findAll(): AssetProxyQueryResultInterface { $query = new PixxioAssetProxyQuery($this->assetSource); @@ -138,10 +112,6 @@ public function findAll(): AssetProxyQueryResultInterface return new PixxioAssetProxyQueryResult($query); } - /** - * @param string $searchTerm - * @return AssetProxyQueryResultInterface - */ public function findBySearchTerm(string $searchTerm): AssetProxyQueryResultInterface { $query = new PixxioAssetProxyQuery($this->assetSource); @@ -152,10 +122,6 @@ public function findBySearchTerm(string $searchTerm): AssetProxyQueryResultInter return new PixxioAssetProxyQueryResult($query); } - /** - * @param Tag $tag - * @return AssetProxyQueryResultInterface - */ public function findByTag(Tag $tag): AssetProxyQueryResultInterface { $query = new PixxioAssetProxyQuery($this->assetSource); @@ -166,9 +132,6 @@ public function findByTag(Tag $tag): AssetProxyQueryResultInterface return new PixxioAssetProxyQueryResult($query); } - /** - * @return AssetProxyQueryResultInterface - */ public function findUntagged(): AssetProxyQueryResultInterface { $query = new PixxioAssetProxyQuery($this->assetSource); @@ -178,33 +141,16 @@ public function findUntagged(): AssetProxyQueryResultInterface return new PixxioAssetProxyQueryResult($query); } - /** - * @return int - */ public function countAll(): int { return (new PixxioAssetProxyQuery($this->assetSource))->count(); } - /** - * Sets the property names to order results by. Expected like this: - * array( - * 'filename' => SupportsSorting::ORDER_ASCENDING, - * 'lastModified' => SupportsSorting::ORDER_DESCENDING - * ) - * - * @param array $orderings The property names to order by by default - * @return void - * @api - */ public function orderBy(array $orderings): void { $this->orderings = $orderings; } - /** - * @return StringFrontend - */ public function getAssetProxyCache(): StringFrontend { if ($this->assetProxyCache instanceof DependencyProxy) { diff --git a/Classes/AssetSource/PixxioAssetSource.php b/Classes/AssetSource/PixxioAssetSource.php index ab2e65a..748f70c 100644 --- a/Classes/AssetSource/PixxioAssetSource.php +++ b/Classes/AssetSource/PixxioAssetSource.php @@ -1,4 +1,5 @@ assetSourceIdentifier; } - /** - * @return string - */ public function getLabel(): string { return 'pixx.io'; } - /** - * @return AssetProxyRepositoryInterface - */ public function getAssetProxyRepository(): AssetProxyRepositoryInterface { if ($this->assetProxyRepository === null) { @@ -239,40 +185,27 @@ public function getAssetProxyRepository(): AssetProxyRepositoryInterface return $this->assetProxyRepository; } - /** - * @return bool - */ public function isReadOnly(): bool { return true; } - /** - * @return array - */ public function getAssetSourceOptions(): array { return $this->assetSourceOptions; } - /** - * @return bool - */ public function isAutoTaggingEnabled(): bool { return $this->autoTaggingEnable; } - /** - * @return string - */ public function getAutoTaggingInUseTag(): string { return $this->autoTaggingInUseTag; } /** - * @return PixxioClient * @throws MissingClientSecretException * @throws AuthenticationFailedException */ @@ -320,6 +253,4 @@ public function getDescription(): string { return $this->description; } - - } diff --git a/Classes/Command/PixxioCommandController.php b/Classes/Command/PixxioCommandController.php index 5c046e8..b1f4493 100644 --- a/Classes/Command/PixxioCommandController.php +++ b/Classes/Command/PixxioCommandController.php @@ -1,4 +1,6 @@ view->assign('apiEndpointUri', $this->assetSourcesConfiguration['flownative-pixxio']['assetSourceOptions']['apiEndpointUri']); $this->view->assign('sharedRefreshToken', $this->assetSourcesConfiguration['flownative-pixxio']['assetSourceOptions']['sharedRefreshToken'] ?? null); @@ -69,11 +66,10 @@ public function indexAction() } /** - * @param ?string|null $refreshToken * @throws IllegalObjectTypeException * @throws UnsupportedRequestTypeException */ - public function updateRefreshTokenAction(string $refreshToken = null) + public function updateRefreshTokenAction(string $refreshToken = null): void { $account = $this->securityContext->getAccount(); $clientSecret = $this->clientSecretRepository->findOneByFlowAccountIdentifier($account->getAccountIdentifier()); diff --git a/Classes/Domain/Model/ClientSecret.php b/Classes/Domain/Model/ClientSecret.php index 76a43ad..b4fe3de 100644 --- a/Classes/Domain/Model/ClientSecret.php +++ b/Classes/Domain/Model/ClientSecret.php @@ -1,4 +1,5 @@ flowAccountIdentifier; } - /** - * @param string $flowAccountIdentifier - */ public function setFlowAccountIdentifier(string $flowAccountIdentifier): void { $this->flowAccountIdentifier = $flowAccountIdentifier; } - /** - * @return string - */ public function getRefreshToken(): string { return $this->refreshToken; } - /** - * @param string $refreshToken - */ public function setRefreshToken(string $refreshToken): void { $this->refreshToken = $refreshToken; } - /** - * @return string|null - */ public function getAccessToken(): ?string { return $this->accessToken; } - /** - * @param string|null $accessToken - */ - public function setAccessToken($accessToken): void + public function setAccessToken(?string $accessToken): void { $this->accessToken = $accessToken; } diff --git a/Classes/Domain/Repository/ClientSecretRepository.php b/Classes/Domain/Repository/ClientSecretRepository.php index 5ad8936..d4e4457 100644 --- a/Classes/Domain/Repository/ClientSecretRepository.php +++ b/Classes/Domain/Repository/ClientSecretRepository.php @@ -1,4 +1,5 @@ __call('findOneByFlowAccountIdentifier', [$accountIdentifier]); diff --git a/Classes/Exception/AccessToAssetDeniedException.php b/Classes/Exception/AccessToAssetDeniedException.php index bd0c5db..83d9d6c 100644 --- a/Classes/Exception/AccessToAssetDeniedException.php +++ b/Classes/Exception/AccessToAssetDeniedException.php @@ -1,4 +1,5 @@ Date: Tue, 10 Sep 2024 16:34:30 +0200 Subject: [PATCH 5/9] Support for multiple pixx.io asset sources --- Classes/AssetSource/PixxioAssetSource.php | 15 +++- Classes/Command/PixxioCommandController.php | 2 +- Classes/Controller/PixxioController.php | 46 ++++++---- Classes/Domain/Model/ClientSecret.php | 17 +++- .../Repository/ClientSecretRepository.php | 11 ++- Configuration/Settings.yaml | 5 +- Migrations/Mysql/Version20240910102102.php | 43 ++++++++++ Resources/Private/Templates/Pixxio/Index.html | 86 +++++++++++-------- 8 files changed, 160 insertions(+), 65 deletions(-) create mode 100644 Migrations/Mysql/Version20240910102102.php diff --git a/Classes/AssetSource/PixxioAssetSource.php b/Classes/AssetSource/PixxioAssetSource.php index 748f70c..b10e51a 100644 --- a/Classes/AssetSource/PixxioAssetSource.php +++ b/Classes/AssetSource/PixxioAssetSource.php @@ -77,11 +77,14 @@ class PixxioAssetSource implements AssetSourceInterface private array $assetSourceOptions; - protected string $iconPath; + protected string $iconPath = 'resource://Flownative.Pixxio/Public/Icons/PixxioWhite.svg'; - protected string $description; + protected string $label = 'pixx.io'; + + protected string $description = ''; /** + * @throws \InvalidArgumentException */ public function __construct(string $assetSourceIdentifier, array $assetSourceOptions) { @@ -152,6 +155,9 @@ public function __construct(string $assetSourceIdentifier, array $assetSourceOpt case 'icon': $this->iconPath = $optionValue; break; + case 'label': + $this->label = $optionValue; + break; case 'description': $this->description = $optionValue; break; @@ -173,7 +179,7 @@ public function getIdentifier(): string public function getLabel(): string { - return 'pixx.io'; + return $this->label; } public function getAssetProxyRepository(): AssetProxyRepositoryInterface @@ -215,7 +221,7 @@ public function getPixxioClient(): PixxioClient if ($this->securityContext->isInitialized() && $this->securityContext->getAccount()) { $account = $this->securityContext->getAccount(); - $clientSecret = $this->clientSecretRepository->findOneByFlowAccountIdentifier($account->getAccountIdentifier()); + $clientSecret = $this->clientSecretRepository->findOneByIdentifiers($this->assetSourceIdentifier, $account->getAccountIdentifier()); } else { $clientSecret = null; $account = new Account(); @@ -225,6 +231,7 @@ public function getPixxioClient(): PixxioClient if (!empty($this->sharedRefreshToken) && ($clientSecret === null || $clientSecret->getRefreshToken() === '')) { $clientSecret = new ClientSecret(); $clientSecret->setRefreshToken($this->sharedRefreshToken); + $clientSecret->setAssetSourceIdentifier($this->assetSourceIdentifier); $clientSecret->setFlowAccountIdentifier('shared'); } diff --git a/Classes/Command/PixxioCommandController.php b/Classes/Command/PixxioCommandController.php index b1f4493..c091804 100644 --- a/Classes/Command/PixxioCommandController.php +++ b/Classes/Command/PixxioCommandController.php @@ -61,7 +61,7 @@ class PixxioCommandController extends CommandController /** * Tag used assets * - * @param string $assetSource Name of the pixxio asset source + * @param string $assetSource Name of the pixx.io asset source * @param bool $quiet If set, only errors will be displayed. * @return void */ diff --git a/Classes/Controller/PixxioController.php b/Classes/Controller/PixxioController.php index 8880fd9..acf2de8 100644 --- a/Classes/Controller/PixxioController.php +++ b/Classes/Controller/PixxioController.php @@ -46,33 +46,44 @@ class PixxioController extends AbstractModuleController public function indexAction(): void { - $this->view->assign('apiEndpointUri', $this->assetSourcesConfiguration['flownative-pixxio']['assetSourceOptions']['apiEndpointUri']); - $this->view->assign('sharedRefreshToken', $this->assetSourcesConfiguration['flownative-pixxio']['assetSourceOptions']['sharedRefreshToken'] ?? null); - $account = $this->securityContext->getAccount(); - $clientSecret = $this->clientSecretRepository->findOneByFlowAccountIdentifier($account->getAccountIdentifier()); - if ($clientSecret !== null && $clientSecret->getRefreshToken()) { - $this->view->assign('refreshToken', $clientSecret->getRefreshToken()); - } - try { - $assetSource = new PixxioAssetSource('flownative-pixxio', $this->assetSourcesConfiguration['flownative-pixxio']['assetSourceOptions']); - $assetSource->getPixxioClient(); - $this->view->assign('connectionSucceeded', true); - } catch (MissingClientSecretException $e) { - } catch (AuthenticationFailedException $e) { - $this->view->assign('authenticationError', $e->getMessage()); + $assetSourcesData = []; + foreach ($this->assetSourcesConfiguration as $assetSourceIdentifier => $assetSourceConfiguration) { + $assetSourceData = []; + if ($assetSourceConfiguration['assetSource'] !== PixxioAssetSource::class) { + continue; + } + + $assetSourceData['label'] = $assetSourceConfiguration['assetSourceOptions']['label'] ?? 'pixx.io Asset Source'; + $assetSourceData['description'] = $assetSourceConfiguration['assetSourceOptions']['description'] ?? null; + $assetSourceData['apiEndpointUri'] = $assetSourceConfiguration['assetSourceOptions']['apiEndpointUri'] ?? null; + $assetSourceData['sharedRefreshToken'] = $assetSourceConfiguration['assetSourceOptions']['sharedRefreshToken'] ?? null; + $clientSecret = $account ? $this->clientSecretRepository->findOneByIdentifiers($assetSourceIdentifier, $account->getAccountIdentifier()) : null; + if ($clientSecret !== null && $clientSecret->getRefreshToken()) { + $assetSourceData['refreshToken'] = $clientSecret->getRefreshToken(); + } + try { + $assetSource = new PixxioAssetSource($assetSourceIdentifier, $assetSourceConfiguration['assetSourceOptions']); + $assetSource->getPixxioClient(); + $assetSourceData['connectionSucceeded'] = true; + } catch (MissingClientSecretException|AuthenticationFailedException $exception) { + $assetSourceData['authenticationError'] = $exception->getMessage(); + } + + $assetSourcesData[$assetSourceIdentifier] = $assetSourceData; } + $this->view->assign('assetSourcesData', $assetSourcesData); } /** * @throws IllegalObjectTypeException * @throws UnsupportedRequestTypeException */ - public function updateRefreshTokenAction(string $refreshToken = null): void + public function updateRefreshTokenAction(string $assetSourceIdentifier, string $refreshToken = null): void { $account = $this->securityContext->getAccount(); - $clientSecret = $this->clientSecretRepository->findOneByFlowAccountIdentifier($account->getAccountIdentifier()); + $clientSecret = $this->clientSecretRepository->findOneByIdentifiers($assetSourceIdentifier, $account->getAccountIdentifier()); if ($refreshToken === null) { if ($clientSecret !== null) { @@ -87,12 +98,13 @@ public function updateRefreshTokenAction(string $refreshToken = null): void $this->clientSecretRepository->update($clientSecret); } else { $clientSecret = new ClientSecret(); + $clientSecret->setAssetSourceIdentifier($assetSourceIdentifier); $clientSecret->setFlowAccountIdentifier($account->getAccountIdentifier()); $clientSecret->setRefreshToken($refreshToken); $clientSecret->setAccessToken(null); $this->clientSecretRepository->add($clientSecret); } - $this->redirectToUri('index'); + $this->redirect('index'); } } diff --git a/Classes/Domain/Model/ClientSecret.php b/Classes/Domain/Model/ClientSecret.php index b4fe3de..ce4117f 100644 --- a/Classes/Domain/Model/ClientSecret.php +++ b/Classes/Domain/Model/ClientSecret.php @@ -16,7 +16,6 @@ use Doctrine\ORM\Mapping as ORM; use Neos\Flow\Annotations\Entity; -use Neos\Flow\Annotations\Identity; /** * @Entity() @@ -24,11 +23,15 @@ class ClientSecret { /** - * @Identity() * @var string */ protected string $flowAccountIdentifier; + /** + * @var string + */ + protected string $assetSourceIdentifier; + /** * @ORM\Column(type="text") * @var string @@ -51,6 +54,16 @@ public function setFlowAccountIdentifier(string $flowAccountIdentifier): void $this->flowAccountIdentifier = $flowAccountIdentifier; } + public function getAssetSourceIdentifier(): string + { + return $this->assetSourceIdentifier; + } + + public function setAssetSourceIdentifier(string $assetSourceIdentifier): void + { + $this->assetSourceIdentifier = $assetSourceIdentifier; + } + public function getRefreshToken(): string { return $this->refreshToken; diff --git a/Classes/Domain/Repository/ClientSecretRepository.php b/Classes/Domain/Repository/ClientSecretRepository.php index d4e4457..79f23fe 100644 --- a/Classes/Domain/Repository/ClientSecretRepository.php +++ b/Classes/Domain/Repository/ClientSecretRepository.php @@ -23,8 +23,15 @@ */ class ClientSecretRepository extends Repository { - public function findOneByFlowAccountIdentifier(string $accountIdentifier): ?ClientSecret + public function findOneByIdentifiers(string $assetSourceIdentifier, string $accountIdentifier): ?ClientSecret { - return $this->__call('findOneByFlowAccountIdentifier', [$accountIdentifier]); + $query = $this->createQuery(); + $query = $query->matching( + $query->logicalAnd( + $query->equals('assetSourceIdentifier', $assetSourceIdentifier), + $query->equals('flowAccountIdentifier', $accountIdentifier) + ) + ); + return $query->execute()->getFirst(); } } diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml index cee494d..50dc6b8 100644 --- a/Configuration/Settings.yaml +++ b/Configuration/Settings.yaml @@ -68,8 +68,11 @@ Neos: # The icon for the asset source icon: 'resource://Flownative.Pixxio/Public/Icons/PixxioWhite.svg' + # The label for the asset source + label: 'pixx.io assets' + # The description for the asset source - description: 'Pixxio assets' + description: '' Neos: modules: diff --git a/Migrations/Mysql/Version20240910102102.php b/Migrations/Mysql/Version20240910102102.php new file mode 100644 index 0000000..5c8ce2f --- /dev/null +++ b/Migrations/Mysql/Version20240910102102.php @@ -0,0 +1,43 @@ +abortIf( + !$this->connection->getDatabasePlatform() instanceof MySqlPlatform, + "Migration can only be executed safely on '\Doctrine\DBAL\Platforms\MySqlPlatform'." + ); + + $this->addSql('DROP INDEX flow_identity_flownative_pixxio_domain_model_clientsecret ON flownative_pixxio_domain_model_clientsecret'); + $this->addSql('ALTER TABLE flownative_pixxio_domain_model_clientsecret ADD assetsourceidentifier VARCHAR(255) NOT NULL'); + $this->addSql("UPDATE flownative_pixxio_domain_model_clientsecret SET assetsourceidentifier = 'flownative-pixxio' WHERE assetsourceidentifier = ''"); + } + + public function down(Schema $schema): void + { + $this->abortIf( + !$this->connection->getDatabasePlatform() instanceof MySqlPlatform, + "Migration can only be executed safely on '\Doctrine\DBAL\Platforms\MySqlPlatform'." + ); + + $this->addSql('ALTER TABLE flownative_pixxio_domain_model_clientsecret DROP assetsourceidentifier'); + $this->addSql('CREATE UNIQUE INDEX flow_identity_flownative_pixxio_domain_model_clientsecret ON flownative_pixxio_domain_model_clientsecret (flowaccountidentifier)'); + } +} diff --git a/Resources/Private/Templates/Pixxio/Index.html b/Resources/Private/Templates/Pixxio/Index.html index fa94c04..0163709 100644 --- a/Resources/Private/Templates/Pixxio/Index.html +++ b/Resources/Private/Templates/Pixxio/Index.html @@ -1,46 +1,56 @@ {namespace neos=Neos\Neos\ViewHelpers}

{neos:backend.translate(id: 'pixxioConnectionSettings', source: 'Main', package: 'Flownative.Pixxio')}

-
- -
-
- - -

{neos:backend.translate(id: 'sharedRefreshTokenExists', source: 'Main', package: 'Flownative.Pixxio')}

-
- -

{neos:backend.translate(id: 'providePersonalRefreshToken', source: 'Main', package: 'Flownative.Pixxio')}
- {neos:backend.translate(id: 'whereToFindRefreshTokenHint', source: 'Main', package: 'Flownative.Pixxio')}

-
-
-
-
- -
- +
+ + + +
+
+
+

{assetSourceData.label} – {assetSourceIdentifier}

+ +
+ {assetSourceData.description}
+
+
+ + +

{neos:backend.translate(id: 'sharedRefreshTokenExists', source: 'Main', package: 'Flownative.Pixxio')}

+
+ +

{neos:backend.translate(id: 'providePersonalRefreshToken', source: 'Main', package: 'Flownative.Pixxio')}
+ {neos:backend.translate(id: 'whereToFindRefreshTokenHint', source: 'Main', package: 'Flownative.Pixxio')}

+
+
+
+
+ +
+ +
-
-
- -
- +
+ +
+ +
-
-
-
- +
+
+ +
-
-
- -

{neos:backend.translate(id: 'connectionSucceeded', source: 'Main', package: 'Flownative.Pixxio')}

-
- -

{authenticationError}

-
+ + +

{neos:backend.translate(id: 'connectionSucceeded', source: 'Main', package: 'Flownative.Pixxio')}

+
+ +

{assetSourceData.authenticationError}

+
+
-
- - + +
+ From 59cc4a7c41551031ed372438f84d37c29951a948 Mon Sep 17 00:00:00 2001 From: Karsten Dambekalns Date: Tue, 10 Sep 2024 16:38:59 +0200 Subject: [PATCH 6/9] Add CLI command to list configured asset sources --- Classes/Command/PixxioCommandController.php | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Classes/Command/PixxioCommandController.php b/Classes/Command/PixxioCommandController.php index c091804..2087e11 100644 --- a/Classes/Command/PixxioCommandController.php +++ b/Classes/Command/PixxioCommandController.php @@ -58,6 +58,30 @@ class PixxioCommandController extends CommandController */ protected array $assetSourcesConfiguration = []; + /** + * List all configured (pixx.io) asset sources + * + * @param bool $showAll If true, all types of asset sources will be shown + * @return void + */ + public function listCommand(bool $showAll = false): void + { + $assetSourcesData = []; + + foreach ($this->assetSourceService->getAssetSources() as $assetSource) { + if ($showAll || $assetSource instanceof PixxioAssetSource) { + $assetSourcesData[] = [ + $assetSource->getIdentifier(), + $assetSource->getLabel(), + get_class($assetSource), + $assetSource->getDescription() + ]; + } + } + + $this->output->outputTable($assetSourcesData, ['Identifier', 'Label', 'Type', 'Description']); + } + /** * Tag used assets * From 63556c306c458763884f5cc600e1332bc2cf9214 Mon Sep 17 00:00:00 2001 From: Karsten Dambekalns Date: Tue, 10 Sep 2024 18:22:38 +0200 Subject: [PATCH 7/9] !!! Drop default `flownative-pixxio` asset source configuration This allows to really use arbitrary pixx.io asset sources without the need to keep one of them as `flownative-pixxio` in the settings. BREAKING CHANGE If you use category mapping, you **must** move the `mapping` setting into the asset source options or into the new `defaults`. --- Classes/AssetSource/PixxioAssetSource.php | 66 +++++++--- Classes/Command/PixxioCommandController.php | 45 +++---- Classes/Controller/PixxioController.php | 2 +- Configuration/Settings.yaml | 114 +++++++---------- README.md | 135 +++++++++----------- Tests/Unit/PixxioCommandControllerTest.php | 33 +++-- 6 files changed, 199 insertions(+), 196 deletions(-) diff --git a/Classes/AssetSource/PixxioAssetSource.php b/Classes/AssetSource/PixxioAssetSource.php index b10e51a..382b2de 100644 --- a/Classes/AssetSource/PixxioAssetSource.php +++ b/Classes/AssetSource/PixxioAssetSource.php @@ -27,6 +27,7 @@ use Neos\Flow\ResourceManagement\ResourceManager; use Neos\Media\Domain\Model\AssetSource\AssetProxyRepositoryInterface; use Neos\Media\Domain\Model\AssetSource\AssetSourceInterface; +use Neos\Utility\Arrays; use Neos\Utility\MediaTypes; class PixxioAssetSource implements AssetSourceInterface @@ -55,10 +56,19 @@ class PixxioAssetSource implements AssetSourceInterface */ protected ResourceManager $resourceManager; - private string $assetSourceIdentifier; + /** + * @Flow\InjectConfiguration(path="defaults", package="Flownative.Pixxio") + */ + protected array $defaultConfiguration = []; private ?PixxioAssetProxyRepository $assetProxyRepository = null; + private ?PixxioClient $pixxioClient = null; + + private string $assetSourceIdentifier; + + private array $assetSourceOptions; + private string $apiEndpointUri; private string $apiKey; @@ -69,24 +79,22 @@ class PixxioAssetSource implements AssetSourceInterface private string $sharedRefreshToken; - private ?PixxioClient $pixxioClient = null; - - private bool $autoTaggingEnable = false; + private bool $autoTaggingEnabled = false; private string $autoTaggingInUseTag = 'used-by-neos'; - private array $assetSourceOptions; - protected string $iconPath = 'resource://Flownative.Pixxio/Public/Icons/PixxioWhite.svg'; protected string $label = 'pixx.io'; protected string $description = ''; + protected array $mapping = []; + /** * @throws \InvalidArgumentException */ - public function __construct(string $assetSourceIdentifier, array $assetSourceOptions) + protected function __construct(string $assetSourceIdentifier, array $assetSourceOptions) { if (preg_match('/^[a-z][a-z0-9-]{0,62}[a-z]$/', $assetSourceIdentifier) !== 1) { throw new \InvalidArgumentException(sprintf('Invalid asset source identifier "%s". The identifier must match /^[a-z][a-z0-9-]{0,62}[a-z]$/', $assetSourceIdentifier), 1525790890); @@ -94,8 +102,13 @@ public function __construct(string $assetSourceIdentifier, array $assetSourceOpt $this->assetSourceIdentifier = $assetSourceIdentifier; $this->assetSourceOptions = $assetSourceOptions; + } - foreach ($assetSourceOptions as $optionName => $optionValue) { + public function initializeObject(): void + { + $this->assetSourceOptions = Arrays::arrayMergeRecursiveOverrule($this->defaultConfiguration['assetSourceOptions'], $this->assetSourceOptions); + + foreach ($this->assetSourceOptions as $optionName => $optionValue) { switch ($optionName) { case 'apiEndpointUri': $uri = new Uri($optionValue); @@ -103,66 +116,81 @@ public function __construct(string $assetSourceIdentifier, array $assetSourceOpt break; case 'apiKey': if (!is_string($optionValue) || empty($optionValue)) { - throw new \InvalidArgumentException(sprintf('Invalid api key specified for Pixx.io asset source %s', $assetSourceIdentifier), 1525792639); + throw new \InvalidArgumentException(sprintf('Invalid api key specified for pixx.io asset source %s', $this->assetSourceIdentifier), 1525792639); } $this->apiKey = $optionValue; break; case 'apiClientOptions': if (!is_array($optionValue)) { - throw new \InvalidArgumentException(sprintf('Invalid api client options specified for Pixx.io asset source %s', $assetSourceIdentifier), 1591605348); + throw new \InvalidArgumentException(sprintf('Invalid api client options specified for pixx.io asset source %s', $this->assetSourceIdentifier), 1591605348); } $this->apiClientOptions = $optionValue; break; case 'imageOptions': if (!is_array($optionValue)) { - throw new \InvalidArgumentException(sprintf('Invalid image options specified for Pixx.io asset source %s', $assetSourceIdentifier), 1591605349); + throw new \InvalidArgumentException(sprintf('Invalid image options specified for pixx.io asset source %s', $this->assetSourceIdentifier), 1591605349); } $this->imageOptions = $optionValue; break; case 'sharedRefreshToken': if (!is_string($optionValue) || empty($optionValue)) { - throw new \InvalidArgumentException(sprintf('Invalid shared refresh token specified for Pixx.io asset source %s', $assetSourceIdentifier), 1528806843); + throw new \InvalidArgumentException(sprintf('Invalid shared refresh token specified for pixx.io asset source %s', $this->assetSourceIdentifier), 1528806843); } $this->sharedRefreshToken = $optionValue; break; case 'mediaTypes': if (!is_array($optionValue)) { - throw new \InvalidArgumentException(sprintf('Invalid media types specified for Pixx.io asset source %s', $assetSourceIdentifier), 1542809628); + throw new \InvalidArgumentException(sprintf('Invalid media types specified for pixx.io asset source %s', $this->assetSourceIdentifier), 1542809628); } foreach ($optionValue as $mediaType => $mediaTypeOptions) { if (MediaTypes::getFilenameExtensionsFromMediaType($mediaType) === []) { - throw new \InvalidArgumentException(sprintf('Unknown media type "%s" specified for Pixx.io asset source %s', $mediaType, $assetSourceIdentifier), 1542809775); + throw new \InvalidArgumentException(sprintf('Unknown media type "%s" specified for pixx.io asset source %s', $mediaType, $this->assetSourceIdentifier), 1542809775); } } break; case 'autoTagging': if (!is_array($optionValue)) { - throw new \InvalidArgumentException(sprintf('Invalid auto tagging configuration specified for Pixx.io asset source %s', $assetSourceIdentifier), 1587561121); + throw new \InvalidArgumentException(sprintf('Invalid auto tagging configuration specified for pixx.io asset source %s', $this->assetSourceIdentifier), 1587561121); } foreach ($optionValue as $autoTaggingOptionName => $autoTaggingOptionValue) { switch ($autoTaggingOptionName) { case 'enable': - $this->autoTaggingEnable = (bool)$autoTaggingOptionValue; + $this->autoTaggingEnabled = (bool)$autoTaggingOptionValue; break; case 'inUseTag': $this->autoTaggingInUseTag = preg_replace('/[^A-Za-z0-9&_+ßäöüÄÖÜ.@ -]+/u', '', (string)$autoTaggingOptionValue); break; default: - throw new \InvalidArgumentException(sprintf('Unknown asset source option "%s" specified for autoTagging in Pixx.io asset source "%s". Please check your settings.', $autoTaggingOptionName, $assetSourceIdentifier), 1587561244); + throw new \InvalidArgumentException(sprintf('Unknown asset source option "%s" specified for autoTagging in pixx.io asset source "%s". Please check your settings.', $autoTaggingOptionName, $this->assetSourceIdentifier), 1587561244); } } break; case 'icon': + if (!is_string($optionValue)) { + throw new \InvalidArgumentException(sprintf('Invalid icon specified for pixx.io asset source %s', $this->assetSourceIdentifier), 1725985121); + } $this->iconPath = $optionValue; break; case 'label': + if (!is_string($optionValue)) { + throw new \InvalidArgumentException(sprintf('Invalid label specified for pixx.io asset source %s', $this->assetSourceIdentifier), 1725985129); + } $this->label = $optionValue; break; case 'description': + if (!is_string($optionValue)) { + throw new \InvalidArgumentException(sprintf('Invalid description specified for pixx.io asset source %s', $this->assetSourceIdentifier), 1725985133); + } $this->description = $optionValue; break; + case 'mapping': + if (!is_array($optionValue)) { + throw new \InvalidArgumentException(sprintf('Invalid mapping options specified for pixx.io asset source %s', $this->assetSourceIdentifier), 1725985041); + } + $this->mapping = $optionValue; + break; default: - throw new \InvalidArgumentException(sprintf('Unknown asset source option "%s" specified for Pixx.io asset source "%s". Please check your settings.', $optionName, $assetSourceIdentifier), 1525790910); + throw new \InvalidArgumentException(sprintf('Unknown asset source option "%s" specified for pixx.io asset source "%s". Please check your settings.', $optionName, $this->assetSourceIdentifier), 1525790910); } } } @@ -203,7 +231,7 @@ public function getAssetSourceOptions(): array public function isAutoTaggingEnabled(): bool { - return $this->autoTaggingEnable; + return $this->autoTaggingEnabled; } public function getAutoTaggingInUseTag(): string diff --git a/Classes/Command/PixxioCommandController.php b/Classes/Command/PixxioCommandController.php index 2087e11..7bc3483 100644 --- a/Classes/Command/PixxioCommandController.php +++ b/Classes/Command/PixxioCommandController.php @@ -48,11 +48,6 @@ class PixxioCommandController extends CommandController */ protected $assetCollectionRepository; - /** - * @Flow\InjectConfiguration(path="mapping", package="Flownative.Pixxio") - */ - protected array $mapping = []; - /** * @Flow\InjectConfiguration(path="assetSources", package="Neos.Media") */ @@ -86,10 +81,10 @@ public function listCommand(bool $showAll = false): void * Tag used assets * * @param string $assetSource Name of the pixx.io asset source - * @param bool $quiet If set, only errors will be displayed. + * @param bool $quiet If set, only errors will be displayed * @return void */ - public function tagUsedAssetsCommand(string $assetSource = 'flownative-pixxio', bool $quiet = false): void + public function tagUsedAssetsCommand(string $assetSource, bool $quiet = false): void { $assetSourceIdentifier = $assetSource; $iterator = $this->assetRepository->findAllIterator(); @@ -97,7 +92,7 @@ public function tagUsedAssetsCommand(string $assetSource = 'flownative-pixxio', !$quiet && $this->outputLine('Tagging used assets of asset source "%s" via Pixxio API:', [$assetSourceIdentifier]); try { - $pixxioAssetSource = new PixxioAssetSource($assetSourceIdentifier, $this->assetSourcesConfiguration[$assetSourceIdentifier]['assetSourceOptions']); + $pixxioAssetSource = PixxioAssetSource::createFromConfiguration($assetSourceIdentifier, $this->assetSourcesConfiguration[$assetSource]['assetSourceOptions']); $pixxioClient = $pixxioAssetSource->getPixxioClient(); } catch (MissingClientSecretException) { $this->outputLine('Authentication error: Missing client secret'); @@ -135,7 +130,7 @@ public function tagUsedAssetsCommand(string $assetSource = 'flownative-pixxio', } if (!$assetProxy instanceof PixxioAssetProxy) { - $this->outputLine(' error Asset "%s" (%s) could not be accessed via Pixxio-API', [$asset->getLabel(), $asset->getIdentifier()]); + $this->outputLine(' error Asset "%s" (%s) could not be accessed via pixx.io API', [$asset->getLabel(), $asset->getIdentifier()]); continue; } @@ -170,18 +165,18 @@ public function tagUsedAssetsCommand(string $assetSource = 'flownative-pixxio', /** * Update metadata * - * @param string $assetSource - * @param bool $quiet + * @param string $assetSource Name of the pixx.io asset source + * @param bool $quiet If set, only errors will be displayed. * @throws IllegalObjectTypeException */ - public function updateMetadataCommand(string $assetSource = 'flownative-pixxio', bool $quiet = false): void + public function updateMetadataCommand(string $assetSource, bool $quiet = false): void { $assetSourceIdentifier = $assetSource; $iterator = $this->assetRepository->findAllIterator(); !$quiet && $this->outputLine('Updating metadata of currently used assets from source "%s":', [$assetSourceIdentifier]); - $pixxioAssetSource = new PixxioAssetSource($assetSourceIdentifier, $this->assetSourcesConfiguration[$assetSourceIdentifier]['assetSourceOptions']); + $pixxioAssetSource = PixxioAssetSource::createFromConfiguration($assetSourceIdentifier, $this->assetSourcesConfiguration[$assetSource]['assetSourceOptions']); $assetProxyRepository = $pixxioAssetSource->getAssetProxyRepository(); assert($assetProxyRepository instanceof PixxioAssetProxyRepository); @@ -208,7 +203,7 @@ public function updateMetadataCommand(string $assetSource = 'flownative-pixxio', } if (!$assetProxy instanceof PixxioAssetProxy) { - $this->outputLine(' error Asset "%s" (%s) could not be accessed via Pixxio-API', [$asset->getLabel(), $asset->getIdentifier()]); + $this->outputLine(' error Asset "%s" (%s) could not be accessed via pixx.io API', [$asset->getLabel(), $asset->getIdentifier()]); continue; } @@ -253,30 +248,30 @@ public function updateMetadataCommand(string $assetSource = 'flownative-pixxio', /** * Import pixx.io categories as asset collections * - * @param string $assetSourceIdentifier Name of the pixx.io asset source (defaults to "flownative-pixxio") - * @param bool $quiet If set, only errors will be displayed. - * @param bool $dryRun If set, no changes will be made. + * @param string $assetSource Name of the pixx.io asset source + * @param bool $quiet If set, only errors will be displayed + * @param bool $dryRun If set, no changes will be made * @return void * @throws IllegalObjectTypeException * @throws ConnectionException */ - public function importCategoriesAsCollectionsCommand(string $assetSourceIdentifier = 'flownative-pixxio', bool $quiet = true, bool $dryRun = false): void + public function importCategoriesAsCollectionsCommand(string $assetSource, bool $quiet = true, bool $dryRun = false): void { !$quiet && $this->outputLine('Importing categories as asset collections via pixx.io API'); try { - $pixxioAssetSource = new PixxioAssetSource($assetSourceIdentifier, $this->assetSourcesConfiguration[$assetSourceIdentifier]['assetSourceOptions']); - $cantoClient = $pixxioAssetSource->getPixxioClient(); + $pixxioAssetSource = PixxioAssetSource::createFromConfiguration($assetSource, $this->assetSourcesConfiguration[$assetSource]['assetSourceOptions']); + $pixxioClient = $pixxioAssetSource->getPixxioClient(); } catch (\Exception) { $this->outputLine('pixx.io client could not be created'); $this->quit(1); } - $response = $cantoClient->getCategories(); + $response = $pixxioClient->getCategories(); $responseObject = Utils::jsonDecode($response->getBody()->getContents()); foreach ($responseObject->categories as $categoryPath) { $categoryPath = ltrim($categoryPath, '/'); - if ($this->shouldBeImportedAsAssetCollection($categoryPath)) { + if ($this->shouldBeImportedAsAssetCollection($pixxioAssetSource, $categoryPath)) { $assetCollection = $this->assetCollectionRepository->findOneByTitle($categoryPath); if ($assetCollection instanceof AssetCollection) { @@ -296,9 +291,9 @@ public function importCategoriesAsCollectionsCommand(string $assetSourceIdentifi !$quiet && $this->outputLine('Import done.'); } - public function shouldBeImportedAsAssetCollection(string $categoryPath): bool + public function shouldBeImportedAsAssetCollection(PixxioAssetSource $assetSource, string $categoryPath): bool { - $categoriesMapping = $this->mapping['categories']; + $categoriesMapping = $assetSource->getAssetSourceOptions()['mapping']['categories']; if (empty($categoriesMapping)) { $this->outputLine('No categories configured for mapping'); $this->quit(1); @@ -307,7 +302,7 @@ public function shouldBeImportedAsAssetCollection(string $categoryPath): bool $categoryPath = ltrim($categoryPath, '/'); // depth limit - if (substr_count($categoryPath, '/') >= $this->mapping['categoriesMaximumDepth']) { + if (substr_count($categoryPath, '/') >= $assetSource->getAssetSourceOptions()['mapping']['categoriesMaximumDepth']) { return false; } diff --git a/Classes/Controller/PixxioController.php b/Classes/Controller/PixxioController.php index acf2de8..a57fc17 100644 --- a/Classes/Controller/PixxioController.php +++ b/Classes/Controller/PixxioController.php @@ -64,7 +64,7 @@ public function indexAction(): void $assetSourceData['refreshToken'] = $clientSecret->getRefreshToken(); } try { - $assetSource = new PixxioAssetSource($assetSourceIdentifier, $assetSourceConfiguration['assetSourceOptions']); + $assetSource = PixxioAssetSource::createFromConfiguration($assetSourceIdentifier, $this->assetSourcesConfiguration[$assetSourceIdentifier]['assetSourceOptions']); $assetSource->getPixxioClient(); $assetSourceData['connectionSucceeded'] = true; } catch (MissingClientSecretException|AuthenticationFailedException $exception) { diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml index 50dc6b8..5fa59e4 100644 --- a/Configuration/Settings.yaml +++ b/Configuration/Settings.yaml @@ -1,78 +1,62 @@ Flownative: Pixxio: - mapping: - # map "categories" from pixx.io to Neos - categoriesMaximumDepth: 10 - categories: [] + defaults: + assetSourceOptions: + # The icon for the asset source + icon: 'resource://Flownative.Pixxio/Public/Icons/PixxioWhite.svg' -Neos: - Flow: - mvc: - routes: - 'Flownative.Pixxio': - position: 'after Neos.Neos' - - Media: - assetSources: - 'flownative-pixxio': - assetSource: 'Flownative\Pixxio\AssetSource\PixxioAssetSource' - assetSourceOptions: - - # The customer-specific endpoint URI pointing to the Pixx.io API: - # Example: 'https://flownative.pixxio.media/cgi-bin/api/pixxio-api.pl' - apiEndpointUri: '' + # The label for the asset source + label: 'pixx.io assets' - # The API key of this Pixx.io integration. - # Please get in touch with Pixx.io support in order to get this key. - apiKey: '' + # A description for the asset source, optional + description: '' - # Options for the Guzzle HTTP Client as specified here - # see http://docs.guzzlephp.org/en/6.5/request-options.html - # Use this to configure custom certificates or to disable cert verification - apiClientOptions: {} + # Additional configuration for specific media types + mediaTypes: + 'image/svg+xml': + usePixxioThumbnailAsOriginal: true + 'application/pdf': + usePixxioThumbnailAsOriginal: true - # A pixx.io user refresh token which is shared across all editors in - # this Neos installation. - # sharedRefreshToken: '' + # This plugin can automatically tag files in the pixxio media library + # when they are in use, and remove the tag once they are not used anymore + autoTagging: + enable: false + inUseTag: 'used-by-neos' - # Additional configuration for specific media types - mediaTypes: - 'image/svg+xml': - usePixxioThumbnailAsOriginal: true - 'application/pdf': - usePixxioThumbnailAsOriginal: true + # map "categories" from pixx.io to Neos + mapping: + categoriesMaximumDepth: 10 + categories: [] - # This plugin can automatically tag files in the pixxio media library - # when they are in use, and remove the tag once they are not used anymore - autoTagging: - enable: false - inUseTag: 'used-by-neos' + # Image options, are the formats returned for use in the asset properties "thumbnailUri", "previewUri" and "originalUri" + # imageOptions parameter are described here: https://tutorial.pixxio.cloud/cgi-bin/api/pixxio-api.pl/documentation/generalInformation/imageOptions + # Setting the "crop" parameter, removes the "height" attribute, and creates a non-cropped version + imageOptions: + thumbnailUri: + width: 400 + height: 400 + quality: 90 + crop: false + previewUri: + width: 1500 + height: 1500 + quality: 90 + originalUri: + sizeMax: 1920 + quality: 90 - # Image options, are the formats returned for use in the asset properties "thumbnailUri", "previewUri" and "originalUri" - # imageOptions parameter are described here: https://tutorial.pixxio.cloud/cgi-bin/api/pixxio-api.pl/documentation/generalInformation/imageOptions - # Setting the "crop" parameter, removes the "height" attribute, and creates a non-cropped version - imageOptions: - thumbnailUri: - width: 400 - height: 400 - quality: 90 - crop: false - previewUri: - width: 1500 - height: 1500 - quality: 90 - originalUri: - sizeMax: 1920 - quality: 90 + # Options for the Guzzle HTTP Client as specified here + # see http://docs.guzzlephp.org/en/6.5/request-options.html + # Use this to configure custom certificates or to disable cert verification + apiClientOptions: {} - # The icon for the asset source - icon: 'resource://Flownative.Pixxio/Public/Icons/PixxioWhite.svg' - - # The label for the asset source - label: 'pixx.io assets' - - # The description for the asset source - description: '' +Neos: + Flow: + mvc: + routes: + 'Flownative.Pixxio': + position: 'after Neos.Neos' Neos: modules: diff --git a/README.md b/README.md index d1f0a16..0104550 100644 --- a/README.md +++ b/README.md @@ -39,34 +39,21 @@ The API access is configured by three components: 1. a setting which contains the customer-specific service endpoint URL 2. a setting providing the pixx.io API key -3. a setting providing the pixx.io user refresh token +3. a setting providing a shared pixx.io user refresh token **To get the needed values for API endpoint and API key, please contact your pixx.io support contact.** -First define the customer-specific service endpoint by adding the URL to your settings: +Using those values configure an asset source by adding this to your settings: ```yaml Neos: Media: assetSources: - 'flownative-pixxio': + # an identifier for your asset source, up to you + 'acme-pixxio': assetSource: 'Flownative\Pixxio\AssetSource\PixxioAssetSource' assetSourceOptions: - apiEndpointUri: 'https://flownative.pixxio.media/cgi-bin/api/pixxio-api.pl' -``` - -You will likely just replace "flownative" by our own subdomain. - -Next, add the pixx.io API key and the refresh token of the pixx.io user you want to connect with Neos: - -```yaml -Neos: - Media: - assetSources: - 'flownative-pixxio': - assetSource: 'Flownative\Pixxio\AssetSource\PixxioAssetSource' - assetSourceOptions: - apiEndpointUri: 'https://flownative.pixxio.media/cgi-bin/api/pixxio-api.pl' + apiEndpointUri: 'https://acme.pixxio.media/cgi-bin/api/pixxio-api.pl' apiKey: 'abcdef123456789' sharedRefreshToken: 'A3ZezMq6Q24X314xbaiq5ewNE5q4Gt' ``` @@ -82,6 +69,24 @@ message with further details). ## Additional configuration options +Defaults for the described settings can be found (and adjusted) in `Flownative.Pixxio.defaults.assetSourceOptions`. + +### Label & description + +You can configure a custom label and description for the asset source like this: + +```yaml +Neos: + Media: + assetSources: + 'acme-pixxio': + assetSourceOptions: + label: 'ACME assets' + description: 'Our custom pixx.io assets source' +``` + +### Additional configuration for specific media types + During import, Neos tries to use a medium-sized version of the original instead of the high resolution file uploaded to pixx.io. This greatly improves import speed and produces good results in most cases. Furthermore, this way some formats, like Adobe Photoshop, can be used seamlessly in Neos without the need to prior converting them into a web-compatible image @@ -94,8 +99,7 @@ SVG or PDF are imported this way. You can add more types through the similar ent Neos: Media: assetSources: - 'flownative-pixxio': - assetSource: 'Flownative\Pixxio\AssetSource\PixxioAssetSource' + 'acme-pixxio': assetSourceOptions: mediaTypes: 'image/svg+xml': @@ -104,16 +108,16 @@ Neos: usePixxioThumbnailAsOriginal: true ``` -Sometimes the API Client needs additional configuration for the tls connection -like custom timeouts or certificates. +### Custom API client options + +Sometimes the API Client needs additional configuration for the tls connection like custom timeouts or certificates. See: http://docs.guzzlephp.org/en/6.5/request-options.html ```yaml Neos: Media: assetSources: - 'flownative-pixxio': - assetSource: 'Flownative\Pixxio\AssetSource\PixxioAssetSource' + 'acme-pixxio': assetSourceOptions: apiClientOptions: 'verify': '/path/to/cert.pem' @@ -126,35 +130,24 @@ Via configuration, you can set what dimensions the returned images must have. Th * `previewUri` used in the detail page of a asset * `originalUri` used for downloading the asset -The configuration is by default +Each can be overridden from your own configuration, by addressing the specific preset key. + +By default, the assets from Pixx.io is returned in a cropped format. When this is the case, +a editor can't see if a asset is horizontal or vertical, when looking in the Media Browser list. +By setting `crop: false` the image will be returned in a not-cropped version, and it's visible +for the editor, to see the assets orientation. ```yaml Neos: Media: assetSources: - 'flownative-pixxio': - assetSource: 'Flownative\Pixxio\AssetSource\PixxioAssetSource' + 'acme-pixxio': assetSourceOptions: imageOptions: thumbnailUri: - width: 400 - height: 400 - quality: 90 crop: false - previewUri: - width: 1500 - height: 1500 - quality: 90 - originalUri: - sizeMax: 1920 - quality: 90 ``` -Each imageOptions can be overridden from your own packages configuration, by addressing the specific preset key. - -By default, the assets from Pixx.io is returned in a cropped format. When this is the case, a editor can't see if a asset is horizontal or vertical, when looking in the Media Browser list. -By setting `crop: false` the image will be returned in a not-cropped version, and it's visible for the editor, to see the assets orientation - ## Cleaning up unused assets Whenever a pixx.io asset is used in Neos, the media file will be copied automatically to the internal Neos asset @@ -165,14 +158,14 @@ storage. While this does not happen automatically, it can be easily automated by In order to clean up unused assets, simply run the following command as often as you like: ```bash -./flow media:removeunused --asset-source flownative-pixxio +./flow media:removeunused --asset-source acme-pixxio ``` If you'd rather like to invoke this command through a cron-job, you can add two additional flags which make this command non-interactive: ```bash -./flow media:removeunused --quiet --assume-yes --asset-source flownative-pixxio +./flow media:removeunused --quiet --assume-yes --asset-source acme-pixxio ``` ## Auto-Tagging @@ -188,12 +181,12 @@ Auto-tagging is configured as follows: Neos: Media: assetSources: - 'flownative-pixxio': - assetSource: 'Flownative\Pixxio\AssetSource\PixxioAssetSource' + 'acme-pixxio': assetSourceOptions: autoTagging: enable: true - inUseTag: 'your-custom-tag' + # optional, used-by-neos is the default tag + inUseTag: 'used-by-neos' ``` Since Neos currently cannot handle auto-tagging reliably during runtime, the job must be done through a @@ -201,9 +194,9 @@ command line command. Simply run the following command for tagging new assets an assets which are not in use anymore: ``` -./flow pixxio:tagusedassets +./flow pixxio:tagusedassets --asset-source acme-pixxio -Tagging used assets of asset source "flownative-pixxio" via Pixxio API: +Tagging used assets of asset source "acme-pixxio" via Pixxio API: (tagged) dana-devolk-1348553-unsplash 358 (1) tagged azamat-zhanisov-1348039-unsplash 354 (1) (tagged) tim-foster-1345174-unsplash 373 (1) @@ -227,48 +220,38 @@ pixx.io offers categories to organize assets in a folder-like structure. Those can be mapped to asset collections and tags in Neos, to make them visible for the users. ---- -**NOTE** -The pixx.io asset source declares itself read-only. Neos does not show asset -collections in the UI for read-only asset sources. This has been changed for -Neos 7.3.0 and up with https://github.com/neos/neos-development-collection/pull/3481 - - If you want to use this feature with older Neos versions, you can use the PR with - [cweagans/composer-patches](https://github.com/cweagans/composer-patches#readme) - or copy the adjusted template into your project and use `Views.yaml` to activate it. ---- - The configuration for the category import looks like this: ```yaml -Flownative: - Pixxio: - mapping: - # map "categories" from pixx.io to Neos - categoriesMaximumDepth: 2 # only include the first two levels of categories - categories: - 'People/Employees': - asAssetCollection: false # ignore this category, put more specific patterns first - 'People*': # the category "path" in pixx.io, shell-style globbing is supported - asAssetCollection: true # map to an asset collection named after the category +Neos: + Media: + assetSources: + 'acme-pixxio': + assetSourceOptions: + mapping: + # map "categories" from pixx.io to Neos + categoriesMaximumDepth: 2 # only include the first two levels of categories (10 is default) + categories: + 'People/Employees': + asAssetCollection: false # ignore this category, put more specific patterns first + 'People*': # the category "path" in pixx.io, shell-style globbing is supported + asAssetCollection: true # map to an asset collection named after the category ``` -- The key used is the category identifier from pixx.io as used in the API, - without leading slash -- `asAssetCollection` set to `true` exposes the category as an asset - collection named like the category. +- The key used is the category identifier from pixx.io as used in the API, without leading slash +- `asAssetCollection` set to `true` exposes the category as an asset collection named like the category. Afterwards, run the following command to update the asset collections, ideally in a cronjob to keep things up-to-date: ```bash -./flow pixxio:importcategoriesascollections +./flow pixxio:importcategoriesascollections --asset-source acme-pixxio ``` To check what a given category would import, you can use a verbose dry-run: ```bash -$ ./flow pixxio:importcategoriesascollections --quiet 0 --dry-run 1 +$ ./flow pixxio:importcategoriesascollections --asset-source acme-pixxio --quiet 0 --dry-run 1 Importing categories as asset collections via pixx.io API o Dokumentation = Kunde A diff --git a/Tests/Unit/PixxioCommandControllerTest.php b/Tests/Unit/PixxioCommandControllerTest.php index b497861..1cf0396 100644 --- a/Tests/Unit/PixxioCommandControllerTest.php +++ b/Tests/Unit/PixxioCommandControllerTest.php @@ -1,28 +1,41 @@ 2, - 'categories' => [ - 'home*' => ['asAssetCollection' => false], - 'Kunde A*' => ['asAssetCollection' => false], - '*' => ['asAssetCollection' => true] + protected static $assetSourceOptions = [ + 'mapping' => [ + 'categoriesMaximumDepth' => 2, + 'categories' => [ + 'home*' => ['asAssetCollection' => false], + 'Kunde A*' => ['asAssetCollection' => false], + '*' => ['asAssetCollection' => true] + ], ], ]; + /** + * @var MockObject|PixxioAssetSource + */ + private $mockAssetSource; + + private MockObject|PixxioCommandController $commandController; + public function setUp(): void { - $this->mockCommandController = $this->getAccessibleMock(PixxioCommandController::class, ['dummy']); - $this->mockCommandController->_set('mapping', self::$mapping); + $this->commandController = new PixxioCommandController(); + +// $this->mockAssetSource = $this->getAccessibleMock(PixxioAssetSource::class, ['getAssetSourceOptions']); + $this->mockAssetSource = $this->getMockBuilder(PixxioAssetSource::class)->disableOriginalConstructor()->onlyMethods(['getAssetSourceOptions'])->getMock(); + $this->mockAssetSource->method('getAssetSourceOptions')->willReturn(self::$assetSourceOptions); } public function categoriesMappingProvider(): array @@ -51,6 +64,6 @@ public function categoriesMappingProvider(): array */ public function shouldBeImportedAsAssetCollectionWorks(string $category, bool $expected): void { - self::assertSame($expected, $this->mockCommandController->_call('shouldBeImportedAsAssetCollection', $category), $category); + self::assertSame($expected, $this->commandController->shouldBeImportedAsAssetCollection($this->mockAssetSource, $category), $category); } } From 94e61e54bbfc84acc26dc8c92e2d9ee0156d50c0 Mon Sep 17 00:00:00 2001 From: Karsten Dambekalns Date: Tue, 10 Sep 2024 22:37:54 +0200 Subject: [PATCH 8/9] Improve note markup in README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0104550..ec2d897 100644 --- a/README.md +++ b/README.md @@ -209,9 +209,11 @@ command. It's important to run the `removeunused`-command *after* the tagging co images will not be untagged in the pixx.io media library. --- + **NOTE** At this point, the auto-tagging feature is not really optimized for performance. The command merely iterates over all assets which were imported from pixx.io and checks if tags need to be updated. + --- ### Category mapping from pixx.io to Neos From 355c2ae7de679832e96e3d67380cecf44c9511b8 Mon Sep 17 00:00:00 2001 From: Karsten Dambekalns Date: Fri, 24 Jan 2025 10:27:21 +0100 Subject: [PATCH 9/9] Further code cleanup --- Classes/AssetSource/PixxioAssetProxy.php | 2 +- Classes/AssetSource/PixxioAssetProxyQuery.php | 4 +- .../PixxioAssetProxyQueryResult.php | 1 - .../PixxioAssetProxyRepository.php | 10 +- Classes/AssetSource/PixxioAssetSource.php | 4 - Classes/Command/PixxioCommandController.php | 92 ++++++++----------- Classes/Controller/PixxioController.php | 5 +- Classes/Service/PixxioClient.php | 8 -- Classes/Service/PixxioServiceFactory.php | 14 --- Migrations/Mysql/Version20180517083014.php | 28 ++++-- Tests/Unit/PixxioCommandControllerTest.php | 6 +- 11 files changed, 69 insertions(+), 105 deletions(-) diff --git a/Classes/AssetSource/PixxioAssetProxy.php b/Classes/AssetSource/PixxioAssetProxy.php index 238e28e..0639a8a 100644 --- a/Classes/AssetSource/PixxioAssetProxy.php +++ b/Classes/AssetSource/PixxioAssetProxy.php @@ -68,7 +68,7 @@ final class PixxioAssetProxy implements AssetProxyInterface, HasRemoteOriginalIn * @Flow\Inject * @var ImportedAssetRepository */ - protected $importedAssetRepository; + protected ImportedAssetRepository $importedAssetRepository; /** * @throws Exception diff --git a/Classes/AssetSource/PixxioAssetProxyQuery.php b/Classes/AssetSource/PixxioAssetProxyQuery.php index 1f3ac0a..9b3de2b 100644 --- a/Classes/AssetSource/PixxioAssetProxyQuery.php +++ b/Classes/AssetSource/PixxioAssetProxyQuery.php @@ -49,15 +49,13 @@ final class PixxioAssetProxyQuery implements AssetProxyQueryInterface /** * @Inject - * @var LoggerInterface */ protected LoggerInterface $logger; /** * @Inject - * @var ThrowableStorageInterface */ - protected $throwableStorage; + protected ThrowableStorageInterface $throwableStorage; public function __construct(PixxioAssetSource $assetSource) { diff --git a/Classes/AssetSource/PixxioAssetProxyQueryResult.php b/Classes/AssetSource/PixxioAssetProxyQueryResult.php index 44e02c8..41016a0 100644 --- a/Classes/AssetSource/PixxioAssetProxyQueryResult.php +++ b/Classes/AssetSource/PixxioAssetProxyQueryResult.php @@ -14,7 +14,6 @@ * source code. */ -use Flownative\Pixxio\Exception\ConnectionException; use Neos\Media\Domain\Model\AssetSource\AssetProxy\AssetProxyInterface; use Neos\Media\Domain\Model\AssetSource\AssetProxyQueryInterface; use Neos\Media\Domain\Model\AssetSource\AssetProxyQueryResultInterface; diff --git a/Classes/AssetSource/PixxioAssetProxyRepository.php b/Classes/AssetSource/PixxioAssetProxyRepository.php index a1b883a..1534e71 100644 --- a/Classes/AssetSource/PixxioAssetProxyRepository.php +++ b/Classes/AssetSource/PixxioAssetProxyRepository.php @@ -80,12 +80,10 @@ public function getAssetProxy(string $identifier): AssetProxyInterface throw new AssetNotFoundException('Asset not found', 1526636260); } if (!isset($responseObject->success) || $responseObject->success !== 'true') { - switch ($responseObject->status) { - case 403: - throw new AccessToAssetDeniedException(sprintf('Failed retrieving asset: %s', $response->help ?? '-') , 1589815740); - default: - throw new AssetNotFoundException(sprintf('Failed retrieving asset, unexpected API response: %s', $response->help ?? '-') , 1589354288); - } + throw match ($responseObject->status) { + 403 => new AccessToAssetDeniedException(sprintf('Failed retrieving asset: %s', $response->help ?? '-'), 1589815740), + default => new AssetNotFoundException(sprintf('Failed retrieving asset, unexpected API response: %s', $response->help ?? '-'), 1589354288), + }; } $this->assetProxyCache->set($cacheEntryIdentifier, Utils::jsonEncode($responseObject, JSON_FORCE_OBJECT)); diff --git a/Classes/AssetSource/PixxioAssetSource.php b/Classes/AssetSource/PixxioAssetSource.php index 382b2de..b3e3322 100644 --- a/Classes/AssetSource/PixxioAssetSource.php +++ b/Classes/AssetSource/PixxioAssetSource.php @@ -34,25 +34,21 @@ class PixxioAssetSource implements AssetSourceInterface { /** * @Flow\Inject - * @var PixxioServiceFactory */ protected PixxioServiceFactory $pixxioServiceFactory; /** * @Flow\Inject - * @var ClientSecretRepository */ protected ClientSecretRepository $clientSecretRepository; /** * @Flow\Inject - * @var Context */ protected Context $securityContext; /** * @Flow\Inject - * @var ResourceManager */ protected ResourceManager $resourceManager; diff --git a/Classes/Command/PixxioCommandController.php b/Classes/Command/PixxioCommandController.php index 7bc3483..7ec3f0a 100644 --- a/Classes/Command/PixxioCommandController.php +++ b/Classes/Command/PixxioCommandController.php @@ -19,34 +19,24 @@ use Neos\Media\Domain\Model\AssetSource\AssetSourceAwareInterface; use Neos\Media\Domain\Repository\AssetCollectionRepository; use Neos\Media\Domain\Repository\AssetRepository; -use Neos\Media\Domain\Repository\TagRepository; use Neos\Media\Domain\Service\AssetSourceService; class PixxioCommandController extends CommandController { /** * @Flow\Inject - * @var AssetRepository */ - protected $assetRepository; + protected AssetRepository $assetRepository; /** * @Flow\Inject - * @var AssetSourceService */ - protected $assetSourceService; + protected AssetSourceService $assetSourceService; /** * @Flow\Inject - * @var TagRepository */ - protected $tagRepository; - - /** - * @Flow\Inject - * @var AssetCollectionRepository - */ - protected $assetCollectionRepository; + protected AssetCollectionRepository $assetCollectionRepository; /** * @Flow\InjectConfiguration(path="assetSources", package="Neos.Media") @@ -87,12 +77,11 @@ public function listCommand(bool $showAll = false): void public function tagUsedAssetsCommand(string $assetSource, bool $quiet = false): void { $assetSourceIdentifier = $assetSource; - $iterator = $this->assetRepository->findAllIterator(); - !$quiet && $this->outputLine('Tagging used assets of asset source "%s" via Pixxio API:', [$assetSourceIdentifier]); try { - $pixxioAssetSource = PixxioAssetSource::createFromConfiguration($assetSourceIdentifier, $this->assetSourcesConfiguration[$assetSource]['assetSourceOptions']); + /** @var PixxioAssetSource $pixxioAssetSource */ + $pixxioAssetSource = PixxioAssetSource::createFromConfiguration($assetSourceIdentifier, $this->assetSourcesConfiguration[$assetSourceIdentifier]['assetSourceOptions']); $pixxioClient = $pixxioAssetSource->getPixxioClient(); } catch (MissingClientSecretException) { $this->outputLine('Authentication error: Missing client secret'); @@ -111,26 +100,10 @@ public function tagUsedAssetsCommand(string $assetSource, bool $quiet = false): assert($assetProxyRepository instanceof PixxioAssetProxyRepository); $assetProxyRepository->getAssetProxyCache()->flush(); + $iterator = $this->assetRepository->findAllIterator(); foreach ($this->assetRepository->iterate($iterator) as $asset) { - if (!$asset instanceof Asset) { - continue; - } - if (!$asset instanceof AssetSourceAwareInterface) { - continue; - } - if ($asset->getAssetSourceIdentifier() !== $assetSourceIdentifier) { - continue; - } - - try { - $assetProxy = $asset->getAssetProxy(); - } catch (AccessToAssetDeniedException $exception) { - $this->outputLine(' error %s', [$exception->getMessage()]); - continue; - } - - if (!$assetProxy instanceof PixxioAssetProxy) { - $this->outputLine(' error Asset "%s" (%s) could not be accessed via pixx.io API', [$asset->getLabel(), $asset->getIdentifier()]); + $assetProxy = $this->getPixxioAssetProxy($asset, $assetSourceIdentifier); + if ($assetProxy === null) { continue; } @@ -185,25 +158,8 @@ public function updateMetadataCommand(string $assetSource, bool $quiet = false): $assetsWereUpdated = false; foreach ($this->assetRepository->iterate($iterator) as $asset) { - if (!$asset instanceof Asset) { - continue; - } - if (!$asset instanceof AssetSourceAwareInterface) { - continue; - } - if ($asset->getAssetSourceIdentifier() !== $assetSourceIdentifier) { - continue; - } - - try { - $assetProxy = $asset->getAssetProxy(); - } catch (AccessToAssetDeniedException $exception) { - $this->outputLine(' error %s', [$exception->getMessage()]); - continue; - } - - if (!$assetProxy instanceof PixxioAssetProxy) { - $this->outputLine(' error Asset "%s" (%s) could not be accessed via pixx.io API', [$asset->getLabel(), $asset->getIdentifier()]); + $assetProxy = $this->getPixxioAssetProxy($asset, $assetSourceIdentifier); + if ($assetProxy === null) { continue; } @@ -260,6 +216,7 @@ public function importCategoriesAsCollectionsCommand(string $assetSource, bool $ !$quiet && $this->outputLine('Importing categories as asset collections via pixx.io API'); try { + /** @var PixxioAssetSource $pixxioAssetSource */ $pixxioAssetSource = PixxioAssetSource::createFromConfiguration($assetSource, $this->assetSourcesConfiguration[$assetSource]['assetSourceOptions']); $pixxioClient = $pixxioAssetSource->getPixxioClient(); } catch (\Exception) { @@ -320,4 +277,31 @@ public function shouldBeImportedAsAssetCollection(PixxioAssetSource $assetSource return false; } + + private function getPixxioAssetProxy(mixed $asset, string $assetSourceIdentifier): ?PixxioAssetProxy + { + if (!$asset instanceof AssetSourceAwareInterface) { + return null; + } + if (!$asset instanceof Asset) { + return null; + } + if ($asset->getAssetSourceIdentifier() !== $assetSourceIdentifier) { + return null; + } + + try { + $assetProxy = $asset->getAssetProxy(); + } catch (AccessToAssetDeniedException $exception) { + $this->outputLine(' error %s', [$exception->getMessage()]); + return null; + } + + if (!$assetProxy instanceof PixxioAssetProxy) { + $this->outputLine(' error Asset "%s" (%s) could not be accessed via pixx.io API', [$asset->getLabel(), $asset->getIdentifier()]); + return null; + } + + return $assetProxy; + } } diff --git a/Classes/Controller/PixxioController.php b/Classes/Controller/PixxioController.php index a57fc17..7a68e2c 100644 --- a/Classes/Controller/PixxioController.php +++ b/Classes/Controller/PixxioController.php @@ -31,7 +31,7 @@ class PixxioController extends AbstractModuleController * @Flow\Inject * @var Context */ - protected $securityContext; + protected Context $securityContext; /** * @Flow\InjectConfiguration(path="assetSources", package="Neos.Media") @@ -42,7 +42,7 @@ class PixxioController extends AbstractModuleController * @Flow\Inject * @var ClientSecretRepository */ - protected $clientSecretRepository; + protected ClientSecretRepository $clientSecretRepository; public function indexAction(): void { @@ -64,6 +64,7 @@ public function indexAction(): void $assetSourceData['refreshToken'] = $clientSecret->getRefreshToken(); } try { + /** @var PixxioAssetSource $assetSource */ $assetSource = PixxioAssetSource::createFromConfiguration($assetSourceIdentifier, $this->assetSourcesConfiguration[$assetSourceIdentifier]['assetSourceOptions']); $assetSource->getPixxioClient(); $assetSourceData['connectionSucceeded'] = true; diff --git a/Classes/Service/PixxioClient.php b/Classes/Service/PixxioClient.php index 9867869..f6236c0 100644 --- a/Classes/Service/PixxioClient.php +++ b/Classes/Service/PixxioClient.php @@ -278,12 +278,4 @@ public function getCategories(): ResponseInterface throw new ConnectionException('Retrieving categories failed: ' . $exception->getMessage(), 1642430939); } } - - /** - * @return string - */ - public function getAccessToken(): string - { - return $this->accessToken; - } } diff --git a/Classes/Service/PixxioServiceFactory.php b/Classes/Service/PixxioServiceFactory.php index cdc81a8..7f4500c 100644 --- a/Classes/Service/PixxioServiceFactory.php +++ b/Classes/Service/PixxioServiceFactory.php @@ -15,8 +15,6 @@ */ use Neos\Flow\Annotations as Flow; -use Neos\Flow\Persistence\PersistenceManagerInterface; -use Neos\Flow\Utility\Environment; /** * Factory for the Pixx.io service class @@ -25,18 +23,6 @@ */ class PixxioServiceFactory { - /** - * @Flow\Inject - * @var Environment - */ - protected $environment; - - /** - * @Flow\Inject - * @var PersistenceManagerInterface - */ - protected $persistenceManager; - /** * Creates a new PixxioClient instance and authenticates against the Pixx.io API * diff --git a/Migrations/Mysql/Version20180517083014.php b/Migrations/Mysql/Version20180517083014.php index 254a74b..09bf28b 100644 --- a/Migrations/Mysql/Version20180517083014.php +++ b/Migrations/Mysql/Version20180517083014.php @@ -1,6 +1,10 @@ abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".'); + $this->abortIf( + !$this->connection->getDatabasePlatform() instanceof MySqlPlatform, + "Migration can only be executed safely on '\Doctrine\DBAL\Platforms\MySqlPlatform'." + ); $this->addSql('CREATE TABLE flownative_pixxio_domain_model_clientsecret (persistence_object_identifier VARCHAR(40) NOT NULL, flowaccountidentifier VARCHAR(255) NOT NULL, refreshtoken LONGTEXT NOT NULL, accesstoken LONGTEXT DEFAULT NULL, UNIQUE INDEX flow_identity_flownative_pixxio_domain_model_clientsecret (flowaccountidentifier), PRIMARY KEY(persistence_object_identifier)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB'); } @@ -34,12 +40,14 @@ public function up(Schema $schema): void /** * @param Schema $schema * @return void - * @throws \Doctrine\DBAL\DBALException - * @throws \Doctrine\DBAL\Migrations\AbortMigrationException + * @throws Exception */ - public function down(Schema $schema): void + public function down(Schema $schema): void { - $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on "mysql".'); + $this->abortIf( + !$this->connection->getDatabasePlatform() instanceof MySqlPlatform, + "Migration can only be executed safely on '\Doctrine\DBAL\Platforms\MySqlPlatform'." + ); $this->addSql('DROP TABLE flownative_pixxio_domain_model_clientsecret'); } diff --git a/Tests/Unit/PixxioCommandControllerTest.php b/Tests/Unit/PixxioCommandControllerTest.php index 1cf0396..7bbc59b 100644 --- a/Tests/Unit/PixxioCommandControllerTest.php +++ b/Tests/Unit/PixxioCommandControllerTest.php @@ -1,4 +1,6 @@ [ 'categoriesMaximumDepth' => 2, 'categories' => [ @@ -29,7 +31,7 @@ class PixxioCommandControllerTest extends UnitTestCase private MockObject|PixxioCommandController $commandController; - public function setUp(): void + protected function setUp(): void { $this->commandController = new PixxioCommandController();