From a3079bb4e116169c46c5af3671c323af87b02ba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20=C4=8Cupi=C4=87?= Date: Tue, 3 Sep 2024 16:44:51 +0200 Subject: [PATCH 01/13] Make provider configuration enum --- bundle/DependencyInjection/Configuration.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bundle/DependencyInjection/Configuration.php b/bundle/DependencyInjection/Configuration.php index 71388e6c..f7f3d126 100644 --- a/bundle/DependencyInjection/Configuration.php +++ b/bundle/DependencyInjection/Configuration.php @@ -32,7 +32,8 @@ private function addProviderSection(ArrayNodeDefinition $rootNode): void { $rootNode ->children() - ->scalarNode('provider') + ->enumNode('provider') + ->values(['cloudinary']) ->defaultValue('cloudinary') ->end() ->scalarNode('account_name') From a2079a14a6b5563cde07a88f56a0c75d5f8e0c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20=C4=8Cupi=C4=87?= Date: Tue, 3 Sep 2024 16:45:37 +0200 Subject: [PATCH 02/13] Add parameter in configuration for Cloudinary folder mode --- bundle/DependencyInjection/Configuration.php | 5 +++++ bundle/DependencyInjection/NetgenRemoteMediaExtension.php | 5 +++++ lib/Core/Provider/Cloudinary/CloudinaryProvider.php | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/bundle/DependencyInjection/Configuration.php b/bundle/DependencyInjection/Configuration.php index f7f3d126..367433b7 100644 --- a/bundle/DependencyInjection/Configuration.php +++ b/bundle/DependencyInjection/Configuration.php @@ -4,6 +4,7 @@ namespace Netgen\Bundle\RemoteMediaBundle\DependencyInjection; +use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryProvider; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -162,6 +163,10 @@ private function addCloudinaryConfiguration(ArrayNodeDefinition $rootNode): void ->scalarNode('encryption_key') ->defaultNull() ->end() + ->enumNode('folder_mode') + ->values([CloudinaryProvider::FOLDER_MODE_DYNAMIC, CloudinaryProvider::FOLDER_MODE_FIXED]) + ->defaultValue(CloudinaryProvider::FOLDER_MODE_DYNAMIC) + ->end() ->end() ->end() ->end(); diff --git a/bundle/DependencyInjection/NetgenRemoteMediaExtension.php b/bundle/DependencyInjection/NetgenRemoteMediaExtension.php index 54adb22e..e7bdc4a9 100644 --- a/bundle/DependencyInjection/NetgenRemoteMediaExtension.php +++ b/bundle/DependencyInjection/NetgenRemoteMediaExtension.php @@ -100,6 +100,11 @@ public function load(array $configs, ContainerBuilder $container): void $container->setAlias('netgen_remote_media.provider.cloudinary.gateway', $cloudinaryGatewayAlias); + $container->setParameter( + 'netgen_remote_media.cloudinary.folder_mode', + $config['cloudinary']['folder_mode'], + ); + $loader->load('default_parameters.yaml'); $loader->load('services/**/*.yaml', 'glob'); } diff --git a/lib/Core/Provider/Cloudinary/CloudinaryProvider.php b/lib/Core/Provider/Cloudinary/CloudinaryProvider.php index 544f8d82..33d1b6b0 100644 --- a/lib/Core/Provider/Cloudinary/CloudinaryProvider.php +++ b/lib/Core/Provider/Cloudinary/CloudinaryProvider.php @@ -35,6 +35,10 @@ final class CloudinaryProvider extends AbstractProvider { + public const FOLDER_MODE_FIXED = 'fixed'; + + public const FOLDER_MODE_DYNAMIC = 'dynamic'; + private const IDENTIFIER = 'cloudinary'; public function __construct( From 75e16a2f9bbb231774be273edcdfd71f172f2cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20=C4=8Cupi=C4=87?= Date: Tue, 3 Sep 2024 16:57:17 +0200 Subject: [PATCH 03/13] Update documentation to add folder_mode setting --- docs/INSTALL.md | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/docs/INSTALL.md b/docs/INSTALL.md index e961c267..6707f13a 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -1,8 +1,6 @@ # Installation instructions for Netgen Remote Media Bundle -## Installation steps - -### Configure the bundle +## Configure the bundle In `config.yml` add basic configuration: @@ -16,10 +14,9 @@ netgen_remote_media: **Note:** Currently `cloudinary` is the only supported provider. -#### Cache configuration - -This bundle has a PSR6 compatible remote media provider for Cloudinary which caches all requests towards Cloudinary to prevent breaking the API rate limit and to improve performance. You can manually configure cache pool as well as desired TTOL: +### Cache configuration +This bundle has a PSR6 compatible remote media provider for Cloudinary which caches all requests towards Cloudinary to prevent breaking the API rate limit and to improve performance. You can manually configure cache pool as well as desired TTL: ```yaml netgen_remote_media: @@ -30,9 +27,27 @@ netgen_remote_media: Above shown are the default used parameters. For more information about creating and configuring cache pools, see https://symfony.com/doc/current/cache.html. -**Warning:** the provider uses tagging functionality to be able to invalidate cache on eg. resource upload, edit or delete. In order to support cache tagging, a corresponding tag-aware pool has to be used. If you use a non-tag-aware pool, tagging will be disabled which means that you will experience some issues while using the bundle. Eg. newly uploaded resource might not be visible immediatelly (until the cache doesn't expire) in the browser or search. +**Warning:** the provider uses tagging functionality to be able to invalidate cache on eg. resource upload, edit or delete. In order to support cache tagging, a corresponding tag-aware pool has to be used. If you use a non-tag-aware pool, tagging will be disabled which means that you will experience some issues while using the bundle. Eg. newly uploaded resource might not be visible immediately (until the cache doesn't expire) in the browser or search. + +### Cloudinary configuration + +#### Folder mode + +On June 4th, 2024, Cloudinary has introduced a setting called `folder_mode` which changes the connection between folders and resource public ID. This required some changes in how this bundle works. You can read more [here](https://cloudinary.com/documentation/folder_modes). + +All new accounts are automatically set to `dynamic` mode and this can't be changed, while old accounts are still in `fixed` mode. This can be changed but once changed to `dynamic`, it can't be switched back. + +So in order to support both modes, there's a parameter with the same name here, and it has to be properly configured. You can check your mode in your Cloudinary dashboard. + +```yaml +netgen_remote_media: + cloudinary: + folder_mode: dynamic +``` + +Default value is `dynamic` and another available mode is `fixed` (for old accounts). -#### Cloudinary configuration +#### Caching and logging requests There are three Cloudinary API gateways implemented: @@ -71,7 +86,7 @@ netgen_remote_media: encryption_key: [YOUR_CLOUDINARY_ENCRYPTION_KEY] ``` -#### Upload prefix +### Upload prefix If you need to change Cloudinary API url (to use eg. GEO specific URLs), there's a parameter `upload_prefix` (set to `https://api.cloudinary.com` by default): @@ -80,7 +95,7 @@ netgen_remote_media: upload_prefix: 'https://api.cloudinary.com' ``` -### Require the bundle +## Require the bundle Run the following from your website root folder: @@ -88,7 +103,7 @@ Run the following from your website root folder: $ composer require netgen/remote-media-bundle:^3.0 ``` -### Activate the bundle +## Activate the bundle Activate the bundle in `config/bundles.php` file by adding it to the array: @@ -100,7 +115,7 @@ return [ ]; ``` -### Add routing +## Add routing This bundle has some internal Symfony routes. In order for them to work, include them in your main `config/routes.yaml`: @@ -109,11 +124,11 @@ netgen_remote_media: resource: "@NetgenRemoteMediaBundle/Resources/config/routing.yml" ``` -### Configure Cloudinary webhook notifications (optional) +## Configure Cloudinary webhook notifications (optional) If you want to be able to manage resources through Cloudinary interface as well, you might want to configure the [Cloudinary webhook notifications](Cloudinary/WEBHOOK_NOTIFICATIONS.md). Read more on the link. -### Clear the caches +## Clear the caches Run the following command: From 9e7e62f0d3b0669303dc8888fbd034150e189f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20=C4=8Cupi=C4=87?= Date: Thu, 5 Sep 2024 19:24:12 +0200 Subject: [PATCH 04/13] Adjust the code to behave differently based on folder_mode setting --- bundle/Resources/config/services/core.yaml | 5 ++++ .../Cloudinary/CloudinaryProvider.php | 29 ++++++++++--------- .../Cloudinary/CloudinaryRemoteId.php | 17 +++++++++-- .../Cloudinary/Factory/RemoteResource.php | 22 ++++++++++++-- .../Gateway/Cache/Psr6CachedGateway.php | 5 ++-- .../Cloudinary/Resolver/SearchExpression.php | 8 +++-- .../Cloudinary/Resolver/UploadOptions.php | 12 ++++++-- 7 files changed, 72 insertions(+), 26 deletions(-) diff --git a/bundle/Resources/config/services/core.yaml b/bundle/Resources/config/services/core.yaml index 7bb7eee9..f255630f 100644 --- a/bundle/Resources/config/services/core.yaml +++ b/bundle/Resources/config/services/core.yaml @@ -27,6 +27,7 @@ services: arguments: - "@netgen_remote_media.provider.cloudinary.gateway.inner" - "@netgen_remote_media.cache.pool" + - "%netgen_remote_media.cloudinary.folder_mode%" - "%netgen_remote_media.cache.ttl%" netgen_remote_media.provider.cloudinary.gateway.logged: @@ -48,6 +49,7 @@ services: - "@netgen_remote_media.provider.cloudinary.resolver.upload_options" - "%netgen_remote_media.named_remote_resources%" - "%netgen_remote_media.named_remote_resource_locations%" + - '%netgen_remote_media.cloudinary.folder_mode%' - "@?logger" netgen_remote_media.provider.cloudinary.converter.resource_type: @@ -85,6 +87,7 @@ services: - '@netgen_remote_media.provider.cloudinary.converter.resource_type' - '@netgen_remote_media.provider.cloudinary.converter.visibility_type' - '@netgen_remote_media.provider.cloudinary.factory.md5_file_hash' + - '%netgen_remote_media.cloudinary.folder_mode%' netgen_remote_media.provider.cloudinary.factory.search_result: class: Netgen\RemoteMedia\Core\Provider\Cloudinary\Factory\SearchResult @@ -103,6 +106,7 @@ services: public: false arguments: - '@netgen_remote_media.provider.cloudinary.converter.visibility_type' + - '%netgen_remote_media.cloudinary.folder_mode%' netgen_remote_media.provider.cloudinary.resolver.search_expression: class: Netgen\RemoteMedia\Core\Provider\Cloudinary\Resolver\SearchExpression @@ -110,6 +114,7 @@ services: arguments: - '@netgen_remote_media.provider.cloudinary.converter.resource_type' - '@netgen_remote_media.provider.cloudinary.converter.visibility_type' + - '%netgen_remote_media.cloudinary.folder_mode%' netgen_remote_media.factory.date_time: class: Netgen\RemoteMedia\Core\Factory\DateTime diff --git a/lib/Core/Provider/Cloudinary/CloudinaryProvider.php b/lib/Core/Provider/Cloudinary/CloudinaryProvider.php index 33d1b6b0..9e8c9ce3 100644 --- a/lib/Core/Provider/Cloudinary/CloudinaryProvider.php +++ b/lib/Core/Provider/Cloudinary/CloudinaryProvider.php @@ -50,8 +50,9 @@ public function __construct( private UploadOptionsResolver $uploadOptionsResolver, array $namedRemoteResources, array $namedRemoteResourceLocations, + private string $folderMode, ?LoggerInterface $logger = null, - bool $shouldDeleteFromRemote = false + bool $shouldDeleteFromRemote = false, ) { parent::__construct( $registry, @@ -121,7 +122,7 @@ public function loadFromRemote(string $remoteId): RemoteResource { try { return $this->gateway->get( - CloudinaryRemoteId::fromRemoteId($remoteId), + CloudinaryRemoteId::fromRemoteId($remoteId, $this->folderMode), ); } catch (InvalidRemoteIdException $exception) { $this->logger->notice('[NGRM][Cloudinary] ' . $exception->getMessage()); @@ -133,7 +134,7 @@ public function loadFromRemote(string $remoteId): RemoteResource public function deleteFromRemote(RemoteResource $resource): void { $this->gateway->delete( - CloudinaryRemoteId::fromRemoteId($resource->getRemoteId()), + CloudinaryRemoteId::fromRemoteId($resource->getRemoteId(), $this->folderMode), ); } @@ -163,12 +164,12 @@ public function updateOnRemote(RemoteResource $resource): void if (count($resource->getTags()) === 0) { $this->gateway->removeAllTagsFromResource( - CloudinaryRemoteId::fromRemoteId($resource->getRemoteId()), + CloudinaryRemoteId::fromRemoteId($resource->getRemoteId(), $this->folderMode), ); } $this->gateway->update( - CloudinaryRemoteId::fromRemoteId($resource->getRemoteId()), + CloudinaryRemoteId::fromRemoteId($resource->getRemoteId(), $this->folderMode), $options, ); } @@ -181,7 +182,7 @@ public function generateDownloadLink(RemoteResource $resource, array $transforma } return $this->gateway->getDownloadLink( - CloudinaryRemoteId::fromRemoteId($resource->getRemoteId()), + CloudinaryRemoteId::fromRemoteId($resource->getRemoteId(), $this->folderMode), $options, $resource instanceof AuthenticatedRemoteResource ? $resource->getToken() : null, ); @@ -189,7 +190,7 @@ public function generateDownloadLink(RemoteResource $resource, array $transforma public function authenticateRemoteResource(RemoteResource $resource, AuthToken $token): AuthenticatedRemoteResource { - $url = $this->gateway->getAuthenticatedUrl(CloudinaryRemoteId::fromRemoteId($resource->getRemoteId()), $token); + $url = $this->gateway->getAuthenticatedUrl(CloudinaryRemoteId::fromRemoteId($resource->getRemoteId(), $this->folderMode), $token); return new AuthenticatedRemoteResource($resource, $url, $token); } @@ -197,7 +198,7 @@ public function authenticateRemoteResource(RemoteResource $resource, AuthToken $ public function authenticateRemoteResourceLocation(RemoteResourceLocation $location, AuthToken $token): RemoteResourceLocation { $url = $this->gateway->getAuthenticatedUrl( - CloudinaryRemoteId::fromRemoteId($location->getRemoteResource()->getRemoteId()), + CloudinaryRemoteId::fromRemoteId($location->getRemoteResource()->getRemoteId(), $this->folderMode), $token, ); @@ -257,7 +258,7 @@ protected function internalUpload(ResourceStruct $resourceStruct): RemoteResourc protected function internalBuildVariation(RemoteResource $resource, array $transformations = []): RemoteResourceVariation { $variationUrl = $this->gateway->getVariationUrl( - CloudinaryRemoteId::fromRemoteId($resource->getRemoteId()), + CloudinaryRemoteId::fromRemoteId($resource->getRemoteId(), $this->folderMode), $transformations, $resource instanceof AuthenticatedRemoteResource ? $resource->getToken() : null, ); @@ -279,7 +280,7 @@ protected function internalBuildVideoThumbnail(RemoteResource $resource, array $ $options['start_offset'] = $startOffset !== null ? $startOffset : 'auto'; $thumbnailUrl = $this->gateway->getVideoThumbnail( - CloudinaryRemoteId::fromRemoteId($resource->getRemoteId()), + CloudinaryRemoteId::fromRemoteId($resource->getRemoteId(), $this->folderMode), $options, $resource instanceof AuthenticatedRemoteResource ? $resource->getToken() : null, ); @@ -312,7 +313,7 @@ protected function generatePictureTag(RemoteResource $resource, array $transform } return $this->gateway->getImageTag( - CloudinaryRemoteId::fromRemoteId($resource->getRemoteId()), + CloudinaryRemoteId::fromRemoteId($resource->getRemoteId(), $this->folderMode), $options, $resource instanceof AuthenticatedRemoteResource ? $resource->getToken() : null, ); @@ -340,7 +341,7 @@ protected function generateVideoTag(RemoteResource $resource, array $transformat } return $this->gateway->getVideoTag( - CloudinaryRemoteId::fromRemoteId($resource->getRemoteId()), + CloudinaryRemoteId::fromRemoteId($resource->getRemoteId(), $this->folderMode), $options, $resource instanceof AuthenticatedRemoteResource ? $resource->getToken() : null, ); @@ -368,7 +369,7 @@ protected function generateVideoThumbnailTag(RemoteResource $resource, array $tr } $thumbnailTag = $this->gateway->getImageTag( - CloudinaryRemoteId::fromRemoteId($resource->getRemoteId()), + CloudinaryRemoteId::fromRemoteId($resource->getRemoteId(), $this->folderMode), $options, $resource instanceof AuthenticatedRemoteResource ? $resource->getToken() : null, ); @@ -392,7 +393,7 @@ protected function generateAudioTag(RemoteResource $resource, array $transformat ]; $tag = $this->gateway->getVideoTag( - CloudinaryRemoteId::fromRemoteId($resource->getRemoteId()), + CloudinaryRemoteId::fromRemoteId($resource->getRemoteId(), $this->folderMode), $options, $resource instanceof AuthenticatedRemoteResource ? $resource->getToken() : null, ); diff --git a/lib/Core/Provider/Cloudinary/CloudinaryRemoteId.php b/lib/Core/Provider/Cloudinary/CloudinaryRemoteId.php index 25d37037..7be927ef 100644 --- a/lib/Core/Provider/Cloudinary/CloudinaryRemoteId.php +++ b/lib/Core/Provider/Cloudinary/CloudinaryRemoteId.php @@ -6,6 +6,7 @@ use Netgen\RemoteMedia\API\Values\Folder; use Netgen\RemoteMedia\Exception\Cloudinary\InvalidRemoteIdException; +use Netgen\RemoteMedia\Exception\NotSupportedException; use function array_pop; use function count; @@ -17,22 +18,24 @@ final class CloudinaryRemoteId public function __construct( private string $type, private string $resourceType, - private string $resourceId + private string $resourceId, + private string $folderMode = CloudinaryProvider::FOLDER_MODE_FIXED, ) {} - public static function fromCloudinaryData(array $data): self + public static function fromCloudinaryData(array $data, string $folderMode = CloudinaryProvider::FOLDER_MODE_FIXED): self { return new self( $data['type'] ?? 'upload', $data['resource_type'] ?? 'image', $data['public_id'], + $folderMode, ); } /** * @throws InvalidRemoteIdException */ - public static function fromRemoteId(string $remoteId): self + public static function fromRemoteId(string $remoteId, string $folderMode = CloudinaryProvider::FOLDER_MODE_FIXED): self { $parts = explode('|', $remoteId); @@ -44,6 +47,7 @@ public static function fromRemoteId(string $remoteId): self $parts[0], $parts[1], $parts[2], + $folderMode, ); } @@ -75,6 +79,13 @@ public function getResourceId(): string public function getFolder(): ?Folder { + if ($this->folderMode !== CloudinaryProvider::FOLDER_MODE_FIXED) { + throw new NotSupportedException( + 'Cloudinary', + sprintf('fetching folder from path in "%s" folder mode', $this->folderMode), + ); + } + $resourceIdParts = explode('/', $this->resourceId); array_pop($resourceIdParts); diff --git a/lib/Core/Provider/Cloudinary/Factory/RemoteResource.php b/lib/Core/Provider/Cloudinary/Factory/RemoteResource.php index 6472acf4..dc0df4bc 100644 --- a/lib/Core/Provider/Cloudinary/Factory/RemoteResource.php +++ b/lib/Core/Provider/Cloudinary/Factory/RemoteResource.php @@ -7,7 +7,9 @@ use Cloudinary\Asset\Media; use Netgen\RemoteMedia\API\Factory\FileHash as FileHashFactoryInterface; use Netgen\RemoteMedia\API\Factory\RemoteResource as RemoteResourceFactoryInterface; +use Netgen\RemoteMedia\API\Values\Folder; use Netgen\RemoteMedia\API\Values\RemoteResource as RemoteResourceValue; +use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryProvider; use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryRemoteId; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Converter\ResourceType as ResourceTypeConverter; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Converter\VisibilityType as VisibilityTypeConverter; @@ -25,14 +27,15 @@ final class RemoteResource implements RemoteResourceFactoryInterface public function __construct( private ResourceTypeConverter $resourceTypeConverter, private VisibilityTypeConverter $visibilityTypeConverter, - private FileHashFactoryInterface $fileHashFactory + private FileHashFactoryInterface $fileHashFactory, + private string $folderMode, ) {} public function create($data): RemoteResourceValue { $this->validateData($data); - $cloudinaryRemoteId = CloudinaryRemoteId::fromCloudinaryData($data); + $cloudinaryRemoteId = CloudinaryRemoteId::fromCloudinaryData($data, $this->folderMode); return new RemoteResourceValue( remoteId: $cloudinaryRemoteId->getRemoteId(), @@ -43,7 +46,7 @@ public function create($data): RemoteResourceValue originalFilename: $this->resolveOriginalFilename($data), version: ($data['version'] ?? null) !== null ? (string) $data['version'] : null, visibility: $this->resolveVisibility($data), - folder: $cloudinaryRemoteId->getFolder(), + folder: $this->resolveFolder($data), size: $data['bytes'] ?? 0, altText: $this->resolveAltText($data), caption: $this->resolveCaption($data), @@ -97,6 +100,19 @@ private function resolveVisibility(array $data): string return $this->visibilityTypeConverter->fromCloudinaryType($type); } + private function resolveFolder(array $data): ?Folder + { + if ($this->folderMode === CloudinaryProvider::FOLDER_MODE_FIXED) { + return CloudinaryRemoteId::fromCloudinaryData($data, $this->folderMode)->getFolder(); + } + + if (($data['asset_folder'] ?? '') === '') { + return null; + } + + return Folder::fromPath($data['asset_folder']); + } + private function resolveAltText(array $data): ?string { if (($data['context']['custom']['alt_text'] ?? null) !== null) { diff --git a/lib/Core/Provider/Cloudinary/Gateway/Cache/Psr6CachedGateway.php b/lib/Core/Provider/Cloudinary/Gateway/Cache/Psr6CachedGateway.php index 5876af2e..60024a79 100644 --- a/lib/Core/Provider/Cloudinary/Gateway/Cache/Psr6CachedGateway.php +++ b/lib/Core/Provider/Cloudinary/Gateway/Cache/Psr6CachedGateway.php @@ -39,7 +39,8 @@ final class Psr6CachedGateway implements CacheableGatewayInterface public function __construct( private GatewayInterface $gateway, private CacheItemPoolInterface $cache, - private int $ttl = 7200 + private string $folderMode, + private int $ttl = 7200, ) {} public function usage(): StatusData @@ -198,7 +199,7 @@ public function upload(string $fileUri, array $options): RemoteResource { $uploadResult = $this->gateway->upload($fileUri, $options); - $this->invalidateResourceCache(CloudinaryRemoteId::fromRemoteId($uploadResult->getRemoteId())); + $this->invalidateResourceCache(CloudinaryRemoteId::fromRemoteId($uploadResult->getRemoteId(), $this->folderMode)); $this->invalidateResourceListCache(); $this->invalidateFoldersCache(); $this->invalidateTagsCache(); diff --git a/lib/Core/Provider/Cloudinary/Resolver/SearchExpression.php b/lib/Core/Provider/Cloudinary/Resolver/SearchExpression.php index 5e3feff8..e35ee17d 100644 --- a/lib/Core/Provider/Cloudinary/Resolver/SearchExpression.php +++ b/lib/Core/Provider/Cloudinary/Resolver/SearchExpression.php @@ -6,6 +6,7 @@ use Netgen\RemoteMedia\API\Search\Query; use Netgen\RemoteMedia\API\Values\RemoteResource; +use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryProvider; use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryRemoteId; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Converter\ResourceType as ResourceTypeConverter; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Converter\VisibilityType as VisibilityTypeConverter; @@ -30,6 +31,7 @@ final class SearchExpression public function __construct( private ResourceTypeConverter $resourceTypeConverter, private VisibilityTypeConverter $visibilityTypeConverter, + private string $folderMode, ) {} public function resolve(Query $query): string @@ -230,7 +232,9 @@ private function resolveFolders(Query $query): ?string return null; } - $folders = array_map(static fn ($value) => sprintf('folder:"%s"', $value), $query->getFolders()); + $key = $this->folderMode === CloudinaryProvider::FOLDER_MODE_DYNAMIC ? 'asset_folder' : 'folder'; + + $folders = array_map(static fn ($value) => sprintf('%s:"%s"', $key, $value), $query->getFolders()); return '(' . implode(' OR ', $folders) . ')'; } @@ -254,7 +258,7 @@ private function resolveResourceIds(Query $query): ?string $resourceIds = array_unique( array_map( - static fn ($remoteId) => CloudinaryRemoteId::fromRemoteId($remoteId)->getResourceId(), + static fn ($remoteId) => CloudinaryRemoteId::fromRemoteId($remoteId, $this->folderMode)->getResourceId(), $query->getRemoteIds(), ), ); diff --git a/lib/Core/Provider/Cloudinary/Resolver/UploadOptions.php b/lib/Core/Provider/Cloudinary/Resolver/UploadOptions.php index 79c43e1b..23833911 100644 --- a/lib/Core/Provider/Cloudinary/Resolver/UploadOptions.php +++ b/lib/Core/Provider/Cloudinary/Resolver/UploadOptions.php @@ -6,6 +6,7 @@ use Netgen\RemoteMedia\API\Upload\FileStruct; use Netgen\RemoteMedia\API\Upload\ResourceStruct; +use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryProvider; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Converter\VisibilityType as VisibilityTypeConverter; use Netgen\RemoteMedia\Exception\MimeCategoryParseException; use Netgen\RemoteMedia\Exception\MimeTypeNotFoundException; @@ -25,6 +26,7 @@ final class UploadOptions { public function __construct( private VisibilityTypeConverter $visibilityTypeConverter, + private string $folderMode, private array $noExtensionMimeTypes = ['image', 'video'], private ?MimeTypesInterface $mimeTypes = null ) { @@ -48,11 +50,11 @@ public function resolve(ResourceStruct $resourceStruct): array $publicId = md5_file($resourceStruct->getFileStruct()->getUri()); } - if ($resourceStruct->getFolder()) { + if ($resourceStruct->getFolder() && $this->folderMode === CloudinaryProvider::FOLDER_MODE_FIXED) { $publicId = $resourceStruct->getFolder()->getPath() . '/' . $publicId; } - return [ + $options = [ 'public_id' => $publicId, 'overwrite' => $resourceStruct->doOverwrite(), 'invalidate' => $resourceStruct->doInvalidate() || $resourceStruct->doOverwrite(), @@ -64,6 +66,12 @@ public function resolve(ResourceStruct $resourceStruct): array 'access_control' => $this->visibilityTypeConverter->toCloudinaryAccessControl($resourceStruct->getVisibility()), 'tags' => $resourceStruct->getTags(), ]; + + if ($resourceStruct->getFolder() && $this->folderMode === CloudinaryProvider::FOLDER_MODE_DYNAMIC) { + $options['folder'] = $resourceStruct->getFolder()->getPath(); + } + + return $options; } private function appendExtension(string $publicId, FileStruct $fileStruct): string From 7373f3dcc8ccc99e874544c533249a1db1ed73a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20=C4=8Cupi=C4=87?= Date: Tue, 17 Sep 2024 20:57:50 +0200 Subject: [PATCH 05/13] Run CS fixer --- lib/Core/Provider/Cloudinary/CloudinaryRemoteId.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Core/Provider/Cloudinary/CloudinaryRemoteId.php b/lib/Core/Provider/Cloudinary/CloudinaryRemoteId.php index 7be927ef..04ac848b 100644 --- a/lib/Core/Provider/Cloudinary/CloudinaryRemoteId.php +++ b/lib/Core/Provider/Cloudinary/CloudinaryRemoteId.php @@ -12,6 +12,7 @@ use function count; use function explode; use function implode; +use function sprintf; final class CloudinaryRemoteId { From 966c6fafcd26855ecd7d2d59e86237552413051f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20=C4=8Cupi=C4=87?= Date: Tue, 17 Sep 2024 21:57:06 +0200 Subject: [PATCH 06/13] Fix unit tests --- .../NetgenRemoteMediaExtension.php | 5 -- .../Cloudinary/Resolver/SearchExpression.php | 3 +- .../NetgenRemoteMediaExtensionTest.php | 16 ++-- .../Cloudinary/CloudinaryProviderTest.php | 2 + .../Cloudinary/CloudinaryRemoteIdTest.php | 35 ++++++++ .../Cloudinary/Factory/RemoteResourceTest.php | 78 ++++++++++++++++-- .../Gateway/Cache/Psr6CachedGatewayTest.php | 3 + .../Gateway/CloudinaryApiGatewayTest.php | 3 + .../Resolver/SearchExpressionTest.php | 62 +++++++++++--- .../Cloudinary/Resolver/UploadOptionsTest.php | 80 +++++++++++++++++-- 10 files changed, 250 insertions(+), 37 deletions(-) diff --git a/bundle/DependencyInjection/NetgenRemoteMediaExtension.php b/bundle/DependencyInjection/NetgenRemoteMediaExtension.php index e7bdc4a9..0ecf75d0 100644 --- a/bundle/DependencyInjection/NetgenRemoteMediaExtension.php +++ b/bundle/DependencyInjection/NetgenRemoteMediaExtension.php @@ -4,7 +4,6 @@ namespace Netgen\Bundle\RemoteMediaBundle\DependencyInjection; -use InvalidArgumentException; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Loader\DelegatingLoader; use Symfony\Component\Config\Loader\LoaderResolver; @@ -40,10 +39,6 @@ public function load(array $configs, ContainerBuilder $container): void $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); - if (!isset($config['provider'])) { - throw new InvalidArgumentException('The "provider" option must be set'); - } - $container->setParameter('netgen_remote_media.remove_unused_resources', $config['remove_unused']); $container->setAlias('netgen_remote_media.provider', 'netgen_remote_media.provider.' . $config['provider']); diff --git a/lib/Core/Provider/Cloudinary/Resolver/SearchExpression.php b/lib/Core/Provider/Cloudinary/Resolver/SearchExpression.php index e35ee17d..b9eb29ae 100644 --- a/lib/Core/Provider/Cloudinary/Resolver/SearchExpression.php +++ b/lib/Core/Provider/Cloudinary/Resolver/SearchExpression.php @@ -256,9 +256,10 @@ private function resolveResourceIds(Query $query): ?string return null; } + $folderMode = $this->folderMode; $resourceIds = array_unique( array_map( - static fn ($remoteId) => CloudinaryRemoteId::fromRemoteId($remoteId, $this->folderMode)->getResourceId(), + static fn ($remoteId) => CloudinaryRemoteId::fromRemoteId($remoteId, $folderMode)->getResourceId(), $query->getRemoteIds(), ), ); diff --git a/tests/bundle/DependencyInjection/NetgenRemoteMediaExtensionTest.php b/tests/bundle/DependencyInjection/NetgenRemoteMediaExtensionTest.php index 542357a7..320c6cfe 100644 --- a/tests/bundle/DependencyInjection/NetgenRemoteMediaExtensionTest.php +++ b/tests/bundle/DependencyInjection/NetgenRemoteMediaExtensionTest.php @@ -4,10 +4,10 @@ namespace Netgen\Bundle\RemoteMediaBundle\Tests\DependencyInjection; -use InvalidArgumentException; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractExtensionTestCase; use Netgen\Bundle\RemoteMediaBundle\DependencyInjection\NetgenRemoteMediaExtension; use PHPUnit\Framework\Attributes\CoversClass; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; #[CoversClass(NetgenRemoteMediaExtension::class)] final class NetgenRemoteMediaExtensionTest extends AbstractExtensionTestCase @@ -17,10 +17,10 @@ public function testItSetsValidContainerParameters(): void $this->setParameter('kernel.bundles', []); $this->load(); - $this->assertContainerBuilderHasParameter('netgen_remote_media.parameters.testprovider.account_name', 'testname'); - $this->assertContainerBuilderHasParameter('netgen_remote_media.parameters.testprovider.account_key', 'testkey'); - $this->assertContainerBuilderHasParameter('netgen_remote_media.parameters.testprovider.account_secret', 'testsecret'); - $this->assertContainerBuilderHasParameter('netgen_remote_media.parameters.testprovider.upload_prefix', 'testprefix'); + $this->assertContainerBuilderHasParameter('netgen_remote_media.parameters.cloudinary.account_name', 'testname'); + $this->assertContainerBuilderHasParameter('netgen_remote_media.parameters.cloudinary.account_key', 'testkey'); + $this->assertContainerBuilderHasParameter('netgen_remote_media.parameters.cloudinary.account_secret', 'testsecret'); + $this->assertContainerBuilderHasParameter('netgen_remote_media.parameters.cloudinary.upload_prefix', 'testprefix'); $this->assertContainerBuilderHasParameter('netgen_remote_media.remove_unused_resources', false); $this->assertContainerBuilderHasParameter('netgen_remote_media.cache.pool_name', 'cache.app'); $this->assertContainerBuilderHasParameter('netgen_remote_media.cache.ttl', 3600); @@ -46,12 +46,12 @@ public function testItSetsValidContainerParameters(): void $this->assertContainerBuilderHasAlias('netgen_remote_media.provider.cloudinary.gateway.inner', 'netgen_remote_media.provider.cloudinary.gateway.api'); $this->assertContainerBuilderHasAlias('netgen_remote_media.provider.cloudinary.gateway', 'netgen_remote_media.provider.cloudinary.gateway.cached'); - $this->assertContainerBuilderHasAlias('netgen_remote_media.provider', 'netgen_remote_media.provider.testprovider'); + $this->assertContainerBuilderHasAlias('netgen_remote_media.provider', 'netgen_remote_media.provider.cloudinary'); } public function testWithoutProviderParameter(): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException(InvalidConfigurationException::class); $this->load(['provider' => null]); } @@ -102,7 +102,7 @@ protected function getContainerExtensions(): array protected function getMinimalConfiguration(): array { return [ - 'provider' => 'testprovider', + 'provider' => 'cloudinary', 'account_name' => 'testname', 'account_key' => 'testkey', 'account_secret' => 'testsecret', diff --git a/tests/lib/Core/Provider/Cloudinary/CloudinaryProviderTest.php b/tests/lib/Core/Provider/Cloudinary/CloudinaryProviderTest.php index 404854cf..87a5a798 100644 --- a/tests/lib/Core/Provider/Cloudinary/CloudinaryProviderTest.php +++ b/tests/lib/Core/Provider/Cloudinary/CloudinaryProviderTest.php @@ -76,11 +76,13 @@ protected function setUp(): void new DateTimeFactory(), new UploadOptionsResolver( new VisibilityTypeConverter(), + CloudinaryProvider::FOLDER_MODE_FIXED, ['image', 'video'], $this->mimeTypes, ), [], [], + CloudinaryProvider::FOLDER_MODE_FIXED, $this->logger, false, ); diff --git a/tests/lib/Core/Provider/Cloudinary/CloudinaryRemoteIdTest.php b/tests/lib/Core/Provider/Cloudinary/CloudinaryRemoteIdTest.php index c75e7984..414fe260 100644 --- a/tests/lib/Core/Provider/Cloudinary/CloudinaryRemoteIdTest.php +++ b/tests/lib/Core/Provider/Cloudinary/CloudinaryRemoteIdTest.php @@ -5,8 +5,10 @@ namespace Netgen\RemoteMedia\Tests\Core\Provider\Cloudinary; use Netgen\RemoteMedia\API\Values\Folder; +use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryProvider; use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryRemoteId; use Netgen\RemoteMedia\Exception\Cloudinary\InvalidRemoteIdException; +use Netgen\RemoteMedia\Exception\NotSupportedException; use Netgen\RemoteMedia\Tests\AbstractTestCase; use PHPUnit\Framework\Attributes\CoversClass; @@ -78,6 +80,39 @@ public function testFromRemoteId(): void ); } + public function testFromRemoteIdInDynamicFolderMode(): void + { + $remoteId = CloudinaryRemoteId::fromRemoteId( + 'private|video|media/videos/my_test_video.mp4', + CloudinaryProvider::FOLDER_MODE_DYNAMIC, + ); + + self::assertSame( + 'private|video|media/videos/my_test_video.mp4', + $remoteId->getRemoteId(), + ); + + self::assertSame( + 'media/videos/my_test_video.mp4', + $remoteId->getResourceId(), + ); + + self::assertSame( + 'video', + $remoteId->getResourceType(), + ); + + self::assertSame( + 'private', + $remoteId->getType(), + ); + + self::expectException(NotSupportedException::class); + self::expectExceptionMessage('Provider "Cloudinary" does not support "fetching folder from path in "dynamic" folder mode".'); + + $remoteId->getFolder(); + } + public function testFromInvalidRemoteId(): void { self::expectException(InvalidRemoteIdException::class); diff --git a/tests/lib/Core/Provider/Cloudinary/Factory/RemoteResourceTest.php b/tests/lib/Core/Provider/Cloudinary/Factory/RemoteResourceTest.php index 898ba441..417bca70 100644 --- a/tests/lib/Core/Provider/Cloudinary/Factory/RemoteResourceTest.php +++ b/tests/lib/Core/Provider/Cloudinary/Factory/RemoteResourceTest.php @@ -7,6 +7,7 @@ use Netgen\RemoteMedia\API\Factory\FileHash as FileHashFactoryInterface; use Netgen\RemoteMedia\API\Values\Folder; use Netgen\RemoteMedia\API\Values\RemoteResource; +use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryProvider; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Converter\ResourceType as ResourceTypeConverter; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Converter\VisibilityType as VisibilityTypeConverter; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Factory\CloudinaryConfiguration; @@ -20,7 +21,9 @@ #[CoversClass(RemoteResourceFactory::class)] final class RemoteResourceTest extends AbstractTestCase { - protected RemoteResourceFactory $remoteResourceFactory; + protected RemoteResourceFactory $fixedFolderModeRemoteResourceFactory; + + protected RemoteResourceFactory $dynamicFolderModeRemoteResourceFactory; protected FileHashFactoryInterface|MockObject $fileHashFactoryMock; @@ -38,15 +41,23 @@ protected function setUp(): void $cloudinaryConfigurationFactory->create(); - $this->remoteResourceFactory = new RemoteResourceFactory( + $this->fixedFolderModeRemoteResourceFactory = new RemoteResourceFactory( + new ResourceTypeConverter(), + new VisibilityTypeConverter(), + $this->fileHashFactoryMock, + CloudinaryProvider::FOLDER_MODE_FIXED, + ); + + $this->dynamicFolderModeRemoteResourceFactory = new RemoteResourceFactory( new ResourceTypeConverter(), new VisibilityTypeConverter(), $this->fileHashFactoryMock, + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ); } #[DataProvider('createDataProvider')] - public function testCreate(array $cloudinaryResponse, RemoteResource $expectedResource): void + public function testCreate(array $cloudinaryResponse, RemoteResource $expectedResource, string $folderMode = CloudinaryProvider::FOLDER_MODE_FIXED): void { if (!($cloudinaryResponse['etag'] ?? null)) { $this->fileHashFactoryMock @@ -56,7 +67,9 @@ public function testCreate(array $cloudinaryResponse, RemoteResource $expectedRe ->willReturn('a522f23sf81aa0afd03387c37e2b6eax'); } - $resource = $this->remoteResourceFactory->create($cloudinaryResponse); + $resource = $folderMode === CloudinaryProvider::FOLDER_MODE_FIXED + ? $this->fixedFolderModeRemoteResourceFactory->create($cloudinaryResponse) + : $this->dynamicFolderModeRemoteResourceFactory->create($cloudinaryResponse); self::assertRemoteResourceSame( $expectedResource, @@ -69,7 +82,7 @@ public function testCreateMissingPublicId(): void self::expectException(InvalidDataException::class); self::expectExceptionMessage('Missing required "public_id" property!'); - $this->remoteResourceFactory->create(['test' => 'test']); + $this->fixedFolderModeRemoteResourceFactory->create(['test' => 'test']); } public function testCreateMissingUrls(): void @@ -77,7 +90,7 @@ public function testCreateMissingUrls(): void self::expectException(InvalidDataException::class); self::expectExceptionMessage('Missing required "secure_url" or "url" property!'); - $this->remoteResourceFactory->create(['public_id' => 'test']); + $this->dynamicFolderModeRemoteResourceFactory->create(['public_id' => 'test']); } public static function createDataProvider(): array @@ -139,6 +152,7 @@ public static function createDataProvider(): array 'source' => 'user_upload', ], ), + CloudinaryProvider::FOLDER_MODE_FIXED, ], [ [ @@ -177,6 +191,7 @@ public static function createDataProvider(): array 'created_at' => '2013-06-23T13:59:18Z', ], ), + CloudinaryProvider::FOLDER_MODE_FIXED, ], [ [ @@ -224,6 +239,7 @@ public static function createDataProvider(): array 'overwritten' => 'false', ], ), + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], [ [ @@ -246,6 +262,7 @@ public static function createDataProvider(): array 'variation1', 'variation2', ], + 'asset_folder' => 'media/test', ], new RemoteResource( remoteId: 'authenticated|video|c87hg9xfxrd4itiim3t0', @@ -256,6 +273,7 @@ public static function createDataProvider(): array originalFilename: 'c87hg9xfxrd4itiim3t0.mp4', version: '1371995958', visibility: 'protected', + folder: Folder::fromPath('media/test'), size: 120253, tags: ['tag1', 'tag2'], metadata: [ @@ -267,6 +285,7 @@ public static function createDataProvider(): array 'overwritten' => 'false', ], ), + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], [ [ @@ -310,6 +329,7 @@ public static function createDataProvider(): array 'test' => 'test', ], ), + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], [ [ @@ -353,6 +373,52 @@ public static function createDataProvider(): array 'test2' => 'test2', ], ), + CloudinaryProvider::FOLDER_MODE_FIXED, + ], + [ + [ + 'public_id' => 'c87hg9xfxrd4itiim3t0', + 'version' => 1371995958, + 'signature' => 'f8645b000be7d717599affc89a068157e4748276', + 'format' => 'zip', + 'resource_type' => 'raw', + 'created_at' => '2011-06-23T13:59:18Z', + 'bytes' => 12025, + 'type' => 'test', + 'secure_url' => 'https://res.cloudinary.com/testcloud/v1371995958/raw/media/raw/new/c87hg9xfxrd4itiim3t0', + 'etag' => 'e522f43cf89aa0afd03387c38e2b6e29', + 'context' => [ + 'test' => 'test', + 'custom' => [ + 'test2' => 'test2', + ], + 'original_filename' => 'test.mp4', + ], + 'asset_folder' => 'media/raw/new', + ], + new RemoteResource( + remoteId: 'test|raw|c87hg9xfxrd4itiim3t0', + type: 'other', + url: 'https://res.cloudinary.com/testcloud/raw/test/c87hg9xfxrd4itiim3t0', + md5: 'e522f43cf89aa0afd03387c38e2b6e29', + name: 'c87hg9xfxrd4itiim3t0', + originalFilename: 'test.mp4', + version: '1371995958', + visibility: 'public', + folder: Folder::fromPath('media/raw/new'), + size: 12025, + tags: [], + metadata: [ + 'signature' => 'f8645b000be7d717599affc89a068157e4748276', + 'format' => 'zip', + 'created_at' => '2011-06-23T13:59:18Z', + ], + context: [ + 'test' => 'test', + 'test2' => 'test2', + ], + ), + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], ]; } diff --git a/tests/lib/Core/Provider/Cloudinary/Gateway/Cache/Psr6CachedGatewayTest.php b/tests/lib/Core/Provider/Cloudinary/Gateway/Cache/Psr6CachedGatewayTest.php index d4b58ba4..7ebc099e 100644 --- a/tests/lib/Core/Provider/Cloudinary/Gateway/Cache/Psr6CachedGatewayTest.php +++ b/tests/lib/Core/Provider/Cloudinary/Gateway/Cache/Psr6CachedGatewayTest.php @@ -10,6 +10,7 @@ use Netgen\RemoteMedia\API\Values\AuthToken; use Netgen\RemoteMedia\API\Values\RemoteResource; use Netgen\RemoteMedia\API\Values\StatusData; +use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryProvider; use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryRemoteId; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Gateway\Cache\Psr6CachedGateway; use Netgen\RemoteMedia\Core\Provider\Cloudinary\GatewayInterface; @@ -51,12 +52,14 @@ protected function setUp(): void $this->taggableCachedGateway = new Psr6CachedGateway( $this->apiGatewayMock, $this->taggableCache, + CloudinaryProvider::FOLDER_MODE_FIXED, self::CACHE_TTL, ); $this->nonTaggableCachedGateway = new Psr6CachedGateway( $this->apiGatewayMock, $this->nonTaggableCache, + CloudinaryProvider::FOLDER_MODE_DYNAMIC, self::CACHE_TTL, ); } diff --git a/tests/lib/Core/Provider/Cloudinary/Gateway/CloudinaryApiGatewayTest.php b/tests/lib/Core/Provider/Cloudinary/Gateway/CloudinaryApiGatewayTest.php index 80359447..085e0195 100644 --- a/tests/lib/Core/Provider/Cloudinary/Gateway/CloudinaryApiGatewayTest.php +++ b/tests/lib/Core/Provider/Cloudinary/Gateway/CloudinaryApiGatewayTest.php @@ -18,6 +18,7 @@ use Netgen\RemoteMedia\API\Values\AuthToken; use Netgen\RemoteMedia\API\Values\RemoteResource; use Netgen\RemoteMedia\API\Values\StatusData; +use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryProvider; use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryRemoteId; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Converter\ResourceType as ResourceTypeConverter; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Converter\VisibilityType as VisibilityTypeConverter; @@ -61,6 +62,7 @@ protected function setUp(): void new SearchExpressionResolver( new ResourceTypeConverter(), new VisibilityTypeConverter(), + CloudinaryProvider::FOLDER_MODE_FIXED, ), new AuthTokenResolver(CloudinaryConfigurationInitializer::ENCRYPTION_KEY), ); @@ -239,6 +241,7 @@ public function testIsEncryptionEnabled(): void new SearchExpressionResolver( new ResourceTypeConverter(), new VisibilityTypeConverter(), + CloudinaryProvider::FOLDER_MODE_FIXED, ), new AuthTokenResolver(), ); diff --git a/tests/lib/Core/Provider/Cloudinary/Resolver/SearchExpressionTest.php b/tests/lib/Core/Provider/Cloudinary/Resolver/SearchExpressionTest.php index 57afdea8..35f3b2c4 100644 --- a/tests/lib/Core/Provider/Cloudinary/Resolver/SearchExpressionTest.php +++ b/tests/lib/Core/Provider/Cloudinary/Resolver/SearchExpressionTest.php @@ -5,6 +5,8 @@ namespace Netgen\RemoteMedia\Tests\Core\Provider\Cloudinary\Resolver; use Netgen\RemoteMedia\API\Search\Query; +use Netgen\RemoteMedia\API\Values\Folder; +use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryProvider; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Converter\ResourceType as ResourceTypeConverter; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Converter\VisibilityType as VisibilityTypeConverter; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Resolver\SearchExpression as SearchExpressionResolver; @@ -15,22 +17,35 @@ #[CoversClass(SearchExpressionResolver::class)] final class SearchExpressionTest extends TestCase { - protected SearchExpressionResolver $resolver; + protected SearchExpressionResolver $fixedFolderModeResolver; + + protected SearchExpressionResolver $dynamicFolderModeResolver; protected function setUp(): void { - $this->resolver = new SearchExpressionResolver( + $this->fixedFolderModeResolver = new SearchExpressionResolver( + new ResourceTypeConverter(), + new VisibilityTypeConverter(), + CloudinaryProvider::FOLDER_MODE_FIXED, + ); + + $this->dynamicFolderModeResolver = new SearchExpressionResolver( new ResourceTypeConverter(), new VisibilityTypeConverter(), + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ); } #[DataProvider('dataProvider')] - public function testResolve(Query $query, string $expression): void + public function testResolve(Query $query, string $expression, string $folderMode = CloudinaryProvider::FOLDER_MODE_FIXED): void { + $actualExpression = $folderMode === CloudinaryProvider::FOLDER_MODE_FIXED + ? $this->fixedFolderModeResolver->resolve($query) + : $this->dynamicFolderModeResolver->resolve($query); + self::assertSame( $expression, - $this->resolver->resolve($query), + $actualExpression, ); } @@ -40,45 +55,62 @@ public static function dataProvider(): array [ new Query(), '', + CloudinaryProvider::FOLDER_MODE_FIXED, ], [ new Query(query: 'search term'), 'search term*', + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], [ - new Query(folders: ['root/images/1']), - '(folder:"root/images/1")', + new Query(folders: [Folder::fromPath('root/images/1')]), + '(asset_folder:"root/images/1")', + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], [ new Query( - folders: ['root/images/1', 'root/videos/2'], + folders: [Folder::fromPath('root/images/1'), Folder::fromPath('root/videos/2')], context: ['type' => ['product_image', 'category_image'], 'source' => 'user_upload'], ), '(folder:"root/images/1" OR folder:"root/videos/2") AND ((context.type="product_image" OR context.type="category_image") AND (context.source="user_upload"))', + CloudinaryProvider::FOLDER_MODE_FIXED, + ], + [ + new Query( + folders: [Folder::fromPath('root/images/1'), Folder::fromPath('root/videos/2')], + context: ['type' => ['product_image', 'category_image'], 'source' => 'user_upload'], + ), + '(asset_folder:"root/images/1" OR asset_folder:"root/videos/2") AND ((context.type="product_image" OR context.type="category_image") AND (context.source="user_upload"))', + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], [ new Query(remoteIds: ['upload|image|root/test/picture1', 'upload|image|root/test/picture2', 'upload|image|root/test/picture3']), '(public_id:"root/test/picture1" OR public_id:"root/test/picture2" OR public_id:"root/test/picture3")', + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], [ new Query(md5s: ['hash1', 'hash2']), '(etag="hash1" OR etag="hash2")', + CloudinaryProvider::FOLDER_MODE_FIXED, ], [ new Query(types: ['video']), '(resource_type:"video")' . ' AND (((!format="aac") AND (!format="aiff") AND (!format="amr") AND (!format="flac")' . ' AND (!format="m4a") AND (!format="mp3") AND (!format="ogg") AND (!format="opus") AND (!format="wav")))', + CloudinaryProvider::FOLDER_MODE_FIXED, ], [ new Query(types: ['document']), '(resource_type:"image" OR resource_type:"raw")' . ' AND ((format="pdf" OR format="doc" OR format="docx" OR format="ppt" OR format="pptx" OR format="txt"))', + CloudinaryProvider::FOLDER_MODE_FIXED, ], [ new Query(types: ['document', 'raw']), '(resource_type:"image" OR resource_type:"raw")' . ' AND ((format="pdf" OR format="doc" OR format="docx" OR format="ppt" OR format="pptx" OR format="txt"))', + CloudinaryProvider::FOLDER_MODE_FIXED, ], [ new Query( @@ -90,28 +122,33 @@ public static function dataProvider(): array . ' AND (!format="mp3") AND (!format="ogg") AND (!format="opus") AND (!format="wav") AND (!format="pdf")' . ' AND (!format="doc") AND (!format="docx") AND (!format="ppt") AND (!format="pptx") AND (!format="txt")))' . ' AND ((context.source="user_upload"))', + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], [ new Query(visibilities: ['public']), '(type:"upload")', + CloudinaryProvider::FOLDER_MODE_FIXED, ], [ new Query(visibilities: ['protected']), '(type:"authenticated")', + CloudinaryProvider::FOLDER_MODE_FIXED, ], [ new Query(tags: ['tech']), '(tags:"tech")', + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], [ new Query(tags: ['tech', 'nature']), '(tags:"tech" OR tags:"nature")', + CloudinaryProvider::FOLDER_MODE_FIXED, ], [ new Query( query: 'android', types: ['video'], - folders: ['root/videos'], + folders: [Folder::fromPath('root/videos')], visibilities: ['public'], tags: ['tech'], ), @@ -122,26 +159,28 @@ public static function dataProvider(): array . ' AND (folder:"root/videos")' . ' AND (type:"upload")' . ' AND (tags:"tech")', + CloudinaryProvider::FOLDER_MODE_FIXED, ], [ new Query( query: 'podcast', types: ['audio'], - folders: ['root/audio'], + folders: [Folder::fromPath('root/audio')], visibilities: ['protected'], ), '(resource_type:"video")' . ' AND ((format="aac" OR format="aiff" OR format="amr" OR format="flac" OR format="m4a"' . ' OR format="mp3" OR format="ogg" OR format="opus" OR format="wav"))' . ' AND podcast*' - . ' AND (folder:"root/audio")' + . ' AND (asset_folder:"root/audio")' . ' AND (type:"authenticated")', + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], [ new Query( query: 'search term', types: ['image', 'video', 'document', 'audio'], - folders: ['root', 'root/test'], + folders: [Folder::fromPath('root'), Folder::fromPath('root/test')], tags: ['tech', 'nature'], remoteIds: ['upload|image|root/test/picture1', 'upload|image|root/test/picture2', 'upload|image|root/test/picture3'], md5s: ['hash1', 'hash2'], @@ -157,6 +196,7 @@ public static function dataProvider(): array . ' AND (public_id:"root/test/picture1" OR public_id:"root/test/picture2" OR public_id:"root/test/picture3")' . ' AND (etag="hash1" OR etag="hash2")' . ' AND ((context.original_filename="picture_*") AND (context.type="product_image" OR context.type="category_image"))', + CloudinaryProvider::FOLDER_MODE_FIXED, ], ]; } diff --git a/tests/lib/Core/Provider/Cloudinary/Resolver/UploadOptionsTest.php b/tests/lib/Core/Provider/Cloudinary/Resolver/UploadOptionsTest.php index d075d82e..1ec96f6a 100644 --- a/tests/lib/Core/Provider/Cloudinary/Resolver/UploadOptionsTest.php +++ b/tests/lib/Core/Provider/Cloudinary/Resolver/UploadOptionsTest.php @@ -7,6 +7,7 @@ use Netgen\RemoteMedia\API\Upload\FileStruct; use Netgen\RemoteMedia\API\Upload\ResourceStruct; use Netgen\RemoteMedia\API\Values\Folder; +use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryProvider; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Converter\VisibilityType as VisibilityTypeConverter; use Netgen\RemoteMedia\Core\Provider\Cloudinary\Resolver\UploadOptions as UploadOptionsResolver; use PHPUnit\Framework\Attributes\CoversClass; @@ -18,7 +19,9 @@ #[CoversClass(UploadOptionsResolver::class)] final class UploadOptionsTest extends TestCase { - protected UploadOptionsResolver $resolver; + protected UploadOptionsResolver $fixedFolderModeResolver; + + protected UploadOptionsResolver $dynamicFolderModeResolver; protected MockObject $mimeTypes; @@ -26,16 +29,29 @@ protected function setUp(): void { $this->mimeTypes = $this->createMock(MimeTypesInterface::class); - $this->resolver = new UploadOptionsResolver( + $this->fixedFolderModeResolver = new UploadOptionsResolver( + new VisibilityTypeConverter(), + CloudinaryProvider::FOLDER_MODE_FIXED, + ['image', 'video'], + $this->mimeTypes, + ); + + $this->dynamicFolderModeResolver = new UploadOptionsResolver( new VisibilityTypeConverter(), + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ['image', 'video'], $this->mimeTypes, ); } #[DataProvider('dataProvider')] - public function testResolve(ResourceStruct $resourceStruct, string $mimeType, array $options, bool $hasExtension = true): void - { + public function testResolve( + ResourceStruct $resourceStruct, + string $mimeType, + array $options, + string $folderMode = CloudinaryProvider::FOLDER_MODE_FIXED, + bool $hasExtension = true, + ): void { if ($hasExtension) { $this->mimeTypes ->expects(self::once()) @@ -44,7 +60,9 @@ public function testResolve(ResourceStruct $resourceStruct, string $mimeType, ar ->willReturn($mimeType); } - $resolvedOptions = $this->resolver->resolve($resourceStruct); + $resolvedOptions = $folderMode === CloudinaryProvider::FOLDER_MODE_FIXED + ? $this->fixedFolderModeResolver->resolve($resourceStruct) + : $this->dynamicFolderModeResolver->resolve($resourceStruct); self::assertSame($options, $resolvedOptions); } @@ -73,6 +91,7 @@ public static function dataProvider(): array 'access_control' => [['access_type' => 'anonymous']], 'tags' => [], ], + CloudinaryProvider::FOLDER_MODE_FIXED, ], [ new ResourceStruct( @@ -95,6 +114,7 @@ public static function dataProvider(): array 'access_control' => [['access_type' => 'anonymous']], 'tags' => [], ], + CloudinaryProvider::FOLDER_MODE_FIXED, ], [ new ResourceStruct( @@ -134,6 +154,48 @@ public static function dataProvider(): array 'access_control' => [['access_type' => 'token']], 'tags' => ['backup'], ], + CloudinaryProvider::FOLDER_MODE_FIXED, + ], + [ + new ResourceStruct( + FileStruct::fromPath('/var/storage/backup.zip'), + 'raw', + Folder::fromPath('files/backups'), + 'protected', + 'latest_backup.zip', + false, + false, + null, + null, + ['backup'], + [ + 'alt' => 'test', + 'original_filename' => 'something.jpg', + 'type' => 'product_image', + 'test' => 'test_value', + ], + ), + 'application/zip', + [ + 'public_id' => 'latest_backup_zip.zip', + 'overwrite' => false, + 'invalidate' => false, + 'discard_original_filename' => true, + 'context' => [ + 'alt' => '', + 'caption' => '', + 'original_filename' => 'latest_backup.zip', + 'type' => 'product_image', + 'test' => 'test_value', + ], + 'type' => 'authenticated', + 'resource_type' => 'raw', + 'access_mode' => 'authenticated', + 'access_control' => [['access_type' => 'token']], + 'tags' => ['backup'], + 'folder' => 'files/backups', + ], + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], [ new ResourceStruct( @@ -166,6 +228,7 @@ public static function dataProvider(): array 'access_control' => [['access_type' => 'token']], 'tags' => ['backup', 'archive'], ], + CloudinaryProvider::FOLDER_MODE_FIXED, ], [ new ResourceStruct( @@ -187,6 +250,7 @@ public static function dataProvider(): array 'access_control' => [['access_type' => 'anonymous']], 'tags' => [], ], + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], [ new ResourceStruct( @@ -217,6 +281,7 @@ public static function dataProvider(): array 'access_control' => [['access_type' => 'anonymous']], 'tags' => [], ], + CloudinaryProvider::FOLDER_MODE_FIXED, ], [ new ResourceStruct( @@ -230,7 +295,7 @@ public static function dataProvider(): array ), 'video/mp4', [ - 'public_id' => 'videos/my_video', + 'public_id' => 'my_video', 'overwrite' => true, 'invalidate' => true, 'discard_original_filename' => true, @@ -244,7 +309,9 @@ public static function dataProvider(): array 'access_mode' => 'authenticated', 'access_control' => [['access_type' => 'token']], 'tags' => [], + 'folder' => 'videos', ], + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], [ new ResourceStruct( @@ -269,6 +336,7 @@ public static function dataProvider(): array 'access_control' => [['access_type' => 'anonymous']], 'tags' => [], ], + CloudinaryProvider::FOLDER_MODE_FIXED, false, ], ]; From 4fd740fdd3d67a3db15f4f0358386d850b0529fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20=C4=8Cupi=C4=87?= Date: Wed, 18 Sep 2024 09:40:43 +0200 Subject: [PATCH 07/13] Use display_name property for name --- .../Provider/Cloudinary/Factory/RemoteResource.php | 14 +++++++++++++- .../Cloudinary/Factory/RemoteResourceTest.php | 4 +++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/Core/Provider/Cloudinary/Factory/RemoteResource.php b/lib/Core/Provider/Cloudinary/Factory/RemoteResource.php index dc0df4bc..6adfdb18 100644 --- a/lib/Core/Provider/Cloudinary/Factory/RemoteResource.php +++ b/lib/Core/Provider/Cloudinary/Factory/RemoteResource.php @@ -42,7 +42,7 @@ public function create($data): RemoteResourceValue type: $this->resolveResourceType($data), url: $this->resolveCorrectUrl($data), md5: $this->resolveMd5($data), - name: pathinfo($cloudinaryRemoteId->getResourceId(), PATHINFO_FILENAME), + name: $this->resolveName($data), originalFilename: $this->resolveOriginalFilename($data), version: ($data['version'] ?? null) !== null ? (string) $data['version'] : null, visibility: $this->resolveVisibility($data), @@ -142,6 +142,18 @@ private function resolveMd5(array $data): string return $this->fileHashFactory->createHash($url); } + private function resolveName(array $data): string + { + $cloudinaryRemoteId = CloudinaryRemoteId::fromCloudinaryData($data); + $nameFromPublicId = pathinfo($cloudinaryRemoteId->getResourceId(), PATHINFO_FILENAME); + + if ($this->folderMode === CloudinaryProvider::FOLDER_MODE_FIXED) { + return $nameFromPublicId; + } + + return $data['display_name'] ?? $nameFromPublicId; + } + private function resolveOriginalFilename(array $data): string { if (($data['context']['custom']['original_filename'] ?? null) !== null) { diff --git a/tests/lib/Core/Provider/Cloudinary/Factory/RemoteResourceTest.php b/tests/lib/Core/Provider/Cloudinary/Factory/RemoteResourceTest.php index 417bca70..878a6dc7 100644 --- a/tests/lib/Core/Provider/Cloudinary/Factory/RemoteResourceTest.php +++ b/tests/lib/Core/Provider/Cloudinary/Factory/RemoteResourceTest.php @@ -157,6 +157,7 @@ public static function createDataProvider(): array [ [ 'public_id' => 'other/c87hg9xfxrd4itiim3t0', + 'display_name' => 'c87hg9affrd4tthg12a2', 'signature' => 'f8645b000be7d717599affc89a068157e4748276', 'format' => 'pdf', 'resource_type' => 'image', @@ -196,6 +197,7 @@ public static function createDataProvider(): array [ [ 'public_id' => 'c87hg9xfxrd4itiim3t0', + 'display_name' => 'my_video.mp4', 'version' => 13711295958, 'signature' => 'f8645b000be7d717599affc89a068157e4748276', 'width' => 864, @@ -223,7 +225,7 @@ public static function createDataProvider(): array type: 'video', url: 'https://res.cloudinary.com/testcloud/video/upload/c87hg9xfxrd4itiim3t0', md5: 'a522f23sf81aa0afd03387c37e2b6eax', - name: 'c87hg9xfxrd4itiim3t0', + name: 'my_video.mp4', originalFilename: 'c87hg9xfxrd4itiim3t0.mp4', version: '13711295958', size: 120253, From b3dac2c6843a471ef34268a412bb0d4b8ba5f979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20=C4=8Cupi=C4=87?= Date: Thu, 19 Sep 2024 19:28:09 +0200 Subject: [PATCH 08/13] Refactor notify controller and add new events --- .../Controller/Callback/Cloudinary/Notify.php | 129 +++- .../config/services/controllers.yaml | 1 + .../Callback/Cloudinary/NotifyTest.php | 616 +++++++++++++++++- 3 files changed, 709 insertions(+), 37 deletions(-) diff --git a/bundle/Controller/Callback/Cloudinary/Notify.php b/bundle/Controller/Callback/Cloudinary/Notify.php index ec75b015..691a9053 100644 --- a/bundle/Controller/Callback/Cloudinary/Notify.php +++ b/bundle/Controller/Callback/Cloudinary/Notify.php @@ -8,8 +8,10 @@ use Cloudinary\Api\Upload\UploadApi; use Doctrine\ORM\EntityManagerInterface; use Netgen\RemoteMedia\API\ProviderInterface; +use Netgen\RemoteMedia\API\Values\Folder; use Netgen\RemoteMedia\API\Values\RemoteResource; use Netgen\RemoteMedia\Core\Provider\Cloudinary\CacheableGatewayInterface; +use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryProvider; use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryRemoteId; use Netgen\RemoteMedia\Core\Provider\Cloudinary\GatewayInterface; use Netgen\RemoteMedia\Core\RequestVerifierInterface; @@ -31,35 +33,23 @@ final class Notify extends AbstractController { private const RESOURCE_UPLOAD = 'upload'; private const RESOURCE_DELETE = 'delete'; + private const RESOURCE_MOVE = 'move'; private const RESOURCE_TAGS_CHANGED = 'resource_tags_changed'; private const RESOURCE_CONTEXT_CHANGED = 'resource_context_changed'; private const RESOURCE_RENAME = 'rename'; + private const RESOURCE_DISPLAY_NAME_CHANGED = 'resource_display_name_changed'; private const FOLDER_CREATE = 'create_folder'; private const FOLDER_DELETE = 'delete_folder'; - - private GatewayInterface $gateway; - - private ProviderInterface $provider; - - private RequestVerifierInterface $signatureVerifier; - - private EntityManagerInterface $entityManager; - - private EventDispatcherInterface $eventDispatcher; + private const FOLDER_MOVE_RENAME = 'move_or_rename_asset_folder'; public function __construct( - GatewayInterface $gateway, - ProviderInterface $provider, - RequestVerifierInterface $signatureVerifier, - EntityManagerInterface $entityManager, - EventDispatcherInterface $eventDispatcher, - ) { - $this->gateway = $gateway; - $this->provider = $provider; - $this->signatureVerifier = $signatureVerifier; - $this->entityManager = $entityManager; - $this->eventDispatcher = $eventDispatcher; - } + private GatewayInterface $gateway, + private ProviderInterface $provider, + private RequestVerifierInterface $signatureVerifier, + private EntityManagerInterface $entityManager, + private EventDispatcherInterface $eventDispatcher, + private string $folderMode, + ) {} public function __invoke(Request $request): Response { @@ -84,6 +74,11 @@ public function __invoke(Request $request): Response break; + case self::RESOURCE_MOVE: + $this->handleResourceMoved($requestContent); + + break; + case self::RESOURCE_TAGS_CHANGED: $this->handleTagsChanged($requestContent); @@ -99,8 +94,14 @@ public function __invoke(Request $request): Response break; + case self::RESOURCE_DISPLAY_NAME_CHANGED: + $this->handleDisplayNameChanged($requestContent); + + break; + case self::FOLDER_CREATE: case self::FOLDER_DELETE: + case self::FOLDER_MOVE_RENAME: $this->handleFoldersChanged(); break; @@ -140,7 +141,7 @@ private function handleResourceUploaded(array $requestContent): void $resource ->setUrl($this->gateway->getDownloadLink($cloudinaryRemoteId)) - ->setName(pathinfo($cloudinaryRemoteId->getResourceId(), PATHINFO_FILENAME)) + ->setName($this->resolveName($requestContent)) ->setVersion((string) $requestContent['version']) ->setSize($requestContent['bytes']) ->setTags($requestContent['tags']); @@ -176,6 +177,42 @@ private function handleResourceDeleted(array $requestContent): void } } + private function handleResourceMoved(array $requestContent): void + { + if ($this->folderMode !== CloudinaryProvider::FOLDER_MODE_DYNAMIC) { + return; + } + + if ($this->gateway instanceof CacheableGatewayInterface) { + $this->gateway->invalidateResourceListCache(); + $this->gateway->invalidateFoldersCache(); + } + + foreach ($requestContent['resources'] ?? [] as $publicId => $resourceData) { + $cloudinaryRemoteId = new CloudinaryRemoteId( + $resourceData['type'], + $resourceData['resource_type'], + (string) $publicId, + ); + + $this->gateway->invalidateResourceCache($cloudinaryRemoteId); + + try { + $resource = $this->provider->loadByRemoteId($cloudinaryRemoteId->getRemoteId()); + } catch (RemoteResourceNotFoundException $e) { + continue; + } + + $resource->setFolder(Folder::fromPath($resourceData['to_asset_folder'])); + + if (($resourceData['display_name'] ?? null) !== null) { + $resource->setName($resourceData['display_name']); + } + + $this->provider->store($resource); + } + } + /** * This method is a bit hacky due to inconsistent Cloudinary API response. */ @@ -216,7 +253,7 @@ private function handleResourceRenamed(array $requestContent): void $resource ->setRemoteId($cloudinaryRemoteId->getRemoteId()) - ->setName(pathinfo($cloudinaryRemoteId->getResourceId(), PATHINFO_FILENAME)) + ->setName($this->resolveName($requestContent)) ->setUrl($this->gateway->getDownloadLink($cloudinaryRemoteId)) ->setFolder($cloudinaryRemoteId->getFolder()); @@ -234,6 +271,41 @@ private function handleResourceRenamed(array $requestContent): void } } + private function handleDisplayNameChanged(array $requestContent): void + { + if ($this->folderMode !== CloudinaryProvider::FOLDER_MODE_DYNAMIC) { + return; + } + + if ($this->gateway instanceof CacheableGatewayInterface) { + $this->gateway->invalidateResourceListCache(); + } + + foreach ($requestContent['resources'] ?? [] as $resourceData) { + $cloudinaryRemoteId = new CloudinaryRemoteId( + $resourceData['type'], + $resourceData['resource_type'], + (string) $resourceData['public_id'], + ); + + if ($this->gateway instanceof CacheableGatewayInterface) { + $this->gateway->invalidateResourceCache($cloudinaryRemoteId); + } + + try { + $resource = $this->provider->loadByRemoteId( + $cloudinaryRemoteId->getRemoteId(), + ); + } catch (RemoteResourceNotFoundException $e) { + continue; + } + + $resource->setName($resourceData['new_display_name']); + + $this->provider->store($resource); + } + } + private function handleTagsChanged(array $requestContent): void { if ($this->gateway instanceof CacheableGatewayInterface) { @@ -382,4 +454,13 @@ private function handleFoldersChanged(): void $this->gateway->invalidateFoldersCache(); } } + + private function resolveName(array $data): string + { + $cloudinaryRemoteId = CloudinaryRemoteId::fromCloudinaryData($data); + + return $this->folderMode === CloudinaryProvider::FOLDER_MODE_FIXED + ? pathinfo($cloudinaryRemoteId->getResourceId(), PATHINFO_FILENAME) + : $data['display_name'] ?? pathinfo($cloudinaryRemoteId->getResourceId(), PATHINFO_FILENAME); + } } diff --git a/bundle/Resources/config/services/controllers.yaml b/bundle/Resources/config/services/controllers.yaml index 50d94b92..30539d60 100644 --- a/bundle/Resources/config/services/controllers.yaml +++ b/bundle/Resources/config/services/controllers.yaml @@ -41,5 +41,6 @@ services: - '@netgen_remote_media.provider.cloudinary.verifier.controller.signature' - '@doctrine.orm.entity_manager' - '@event_dispatcher' + - '%netgen_remote_media.cloudinary.folder_mode%' calls: - [setContainer, ['@service_container']] diff --git a/tests/bundle/Controller/Callback/Cloudinary/NotifyTest.php b/tests/bundle/Controller/Callback/Cloudinary/NotifyTest.php index 11c4183e..78b994b3 100644 --- a/tests/bundle/Controller/Callback/Cloudinary/NotifyTest.php +++ b/tests/bundle/Controller/Callback/Cloudinary/NotifyTest.php @@ -7,8 +7,10 @@ use Doctrine\ORM\EntityManagerInterface; use Netgen\Bundle\RemoteMediaBundle\Controller\Callback\Cloudinary\Notify as NotifyController; use Netgen\RemoteMedia\API\ProviderInterface; +use Netgen\RemoteMedia\API\Values\Folder; use Netgen\RemoteMedia\API\Values\RemoteResource; use Netgen\RemoteMedia\Core\Provider\Cloudinary\CacheableGatewayInterface; +use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryProvider; use Netgen\RemoteMedia\Core\Provider\Cloudinary\CloudinaryRemoteId; use Netgen\RemoteMedia\Core\RequestVerifierInterface; use Netgen\RemoteMedia\Event\Cloudinary\NotificationReceivedEvent; @@ -29,7 +31,9 @@ #[CoversClass(NotifyController::class)] final class NotifyTest extends TestCase { - private NotifyController $controller; + private NotifyController $fixedFolderModeController; + + private NotifyController $dynamicFolderModeController; private CacheableGatewayInterface|MockObject $gatewayMock; @@ -49,12 +53,22 @@ protected function setUp(): void $this->entityManagerMock = $this->createMock(EntityManagerInterface::class); $this->eventDispatcherMock = $this->createMock(EventDispatcherInterface::class); - $this->controller = new NotifyController( + $this->fixedFolderModeController = new NotifyController( + $this->gatewayMock, + $this->providerMock, + $this->signatureVerifierMock, + $this->entityManagerMock, + $this->eventDispatcherMock, + CloudinaryProvider::FOLDER_MODE_FIXED, + ); + + $this->dynamicFolderModeController = new NotifyController( $this->gatewayMock, $this->providerMock, $this->signatureVerifierMock, $this->entityManagerMock, $this->eventDispatcherMock, + CloudinaryProvider::FOLDER_MODE_DYNAMIC, ); } @@ -68,7 +82,7 @@ public function testUnverified(): void ->with($request) ->willReturn(false); - $response = $this->controller->__invoke($request); + $response = $this->fixedFolderModeController->__invoke($request); self::assertInstanceOf( JsonResponse::class, @@ -166,7 +180,7 @@ public function testResourceUploaded(): void ->with($cloudinaryRemoteId->getRemoteId()) ->willThrowException(new RemoteResourceNotFoundException($cloudinaryRemoteId->getRemoteId())); - $response = $this->controller->__invoke($request); + $response = $this->fixedFolderModeController->__invoke($request); self::assertInstanceOf( JsonResponse::class, @@ -281,7 +295,7 @@ public function testResourceRewritten(): void ->with($resource) ->willReturn($resource); - $response = $this->controller->__invoke($request); + $response = $this->fixedFolderModeController->__invoke($request); self::assertInstanceOf( JsonResponse::class, @@ -404,7 +418,12 @@ public function testResourcesDeleted(): void static fn (string $remoteId): ?RemoteResource => match ($remoteId) { $cloudinaryRemoteId1->getRemoteId() => $resource, $cloudinaryRemoteId2->getRemoteId() => $resource2, - default => null, + default => throw new RuntimeException( + sprintf( + 'Failed asserting that argument #1 for method "loadByRemoteId" with value "%s" matches one of the expecting values.', + $resource->getRemoteId(), + ), + ), }, ); @@ -423,7 +442,7 @@ public function testResourcesDeleted(): void }, ); - $response = $this->controller->__invoke($request); + $response = $this->fixedFolderModeController->__invoke($request); self::assertInstanceOf( JsonResponse::class, @@ -509,7 +528,508 @@ public function testResourceDeletedNotFound(): void ->with($cloudinaryRemoteId->getRemoteId()) ->willThrowException(new RemoteResourceNotFoundException($cloudinaryRemoteId->getRemoteId())); - $response = $this->controller->__invoke($request); + $response = $this->fixedFolderModeController->__invoke($request); + + self::assertInstanceOf( + JsonResponse::class, + $response, + ); + + self::assertSame( + '"Notification handled."', + $response->getContent(), + ); + + self::assertSame( + Response::HTTP_OK, + $response->getStatusCode(), + ); + } + + public function testResourceMovedFixed(): void + { + $body = json_encode([ + 'notification_type' => 'move', + 'resources' => [ + 'sample' => [ + 'resource_type' => 'image', + 'type' => 'upload', + 'from_asset_folder' => 'clothing', + 'to_asset_folder' => 'clothing_sale', + 'display_name' => 'blue_sweater', + ], + ], + ]); + + $request = new Request( + [], + [], + [], + [], + [], + [], + $body, + ); + + $request->headers->add( + [ + 'x-cld-timestamp' => time(), + 'x-cld-signature' => 'test', + ], + ); + + $this->signatureVerifierMock + ->expects(self::once()) + ->method('verify') + ->with($request) + ->willReturn(true); + + $event = new NotificationReceivedEvent($request); + + $this->eventDispatcherMock + ->expects(self::once()) + ->method('dispatch') + ->with($event, $event::NAME); + + $this->gatewayMock + ->expects(self::never()) + ->method('invalidateResourceListCache'); + + $this->gatewayMock + ->expects(self::never()) + ->method('invalidateFoldersCache'); + + $this->gatewayMock + ->expects(self::never()) + ->method('invalidateResourceCache'); + + $this->providerMock + ->expects(self::never()) + ->method('loadByRemoteId'); + + $this->providerMock + ->expects(self::never()) + ->method('store'); + + $response = $this->fixedFolderModeController->__invoke($request); + + self::assertInstanceOf( + JsonResponse::class, + $response, + ); + + self::assertSame( + '"Notification handled."', + $response->getContent(), + ); + + self::assertSame( + Response::HTTP_OK, + $response->getStatusCode(), + ); + } + + public function testResourceMovedDynamic(): void + { + $body = json_encode([ + 'notification_type' => 'move', + 'resources' => [ + 'blue_sweater' => [ + 'resource_type' => 'image', + 'type' => 'upload', + 'from_asset_folder' => 'clothing', + 'to_asset_folder' => 'clothing_sale', + 'display_name' => 'blue_sweater', + ], + 'red_shirt' => [ + 'resource_type' => 'image', + 'type' => 'upload', + 'from_asset_folder' => 'shirts', + 'to_asset_folder' => 'old_shirts', + 'display_name' => 'red shirt', + ], + 'non_existing_shirt' => [ + 'resource_type' => 'image', + 'type' => 'upload', + 'from_asset_folder' => 'shirts', + 'to_asset_folder' => 'old_shirts', + 'display_name' => 'some shirt', + ], + 'black_pants' => [ + 'resource_type' => 'video', + 'type' => 'upload', + 'from_asset_folder' => 'clothing/pants', + 'to_asset_folder' => 'clothing_sale/pants', + 'display_name' => 'Black pants', + ], + ], + ]); + + $request = new Request( + [], + [], + [], + [], + [], + [], + $body, + ); + + $request->headers->add( + [ + 'x-cld-timestamp' => time(), + 'x-cld-signature' => 'test', + ], + ); + + $this->signatureVerifierMock + ->expects(self::once()) + ->method('verify') + ->with($request) + ->willReturn(true); + + $event = new NotificationReceivedEvent($request); + + $this->eventDispatcherMock + ->expects(self::once()) + ->method('dispatch') + ->with($event, $event::NAME); + + $this->gatewayMock + ->expects(self::once()) + ->method('invalidateResourceListCache'); + + $this->gatewayMock + ->expects(self::once()) + ->method('invalidateFoldersCache'); + + $this->gatewayMock + ->expects(self::exactly(4)) + ->method('invalidateResourceCache') + ->willReturnCallback( + static fn (CloudinaryRemoteId $cloudinaryRemoteId) => match ($cloudinaryRemoteId->getRemoteId()) { + 'upload|image|blue_sweater', 'upload|image|red_shirt', 'upload|image|non_existing_shirt', 'upload|video|black_pants' => null, + default => throw new RuntimeException( + sprintf( + 'Failed asserting that argument #1 for method "invalidateResourceCache" with value "%s" matches one of the expecting values.', + $cloudinaryRemoteId->getRemoteId(), + ), + ), + }, + ); + + $blueSweater = new RemoteResource( + remoteId: 'upload|image|blue_sweater', + type: 'image', + url: 'https://res.cloudinary.com/demo/image/upload/blue_sweater', + md5: 'r43tr4t45454324342', + id: 5, + name: 'Sweater (blue)', + folder: Folder::fromPath('clothing'), + size: 380250, + ); + + $redShirt = new RemoteResource( + remoteId: 'upload|image|red_shirt', + type: 'image', + url: 'https://res.cloudinary.com/demo/video/image/red_shirt', + md5: '3r43456fdgregregre', + id: 6, + name: 'red shirt', + folder: Folder::fromPath('shirts'), + size: 3802350, + ); + + $blackPants = new RemoteResource( + remoteId: 'upload|video|black_pants', + type: 'video', + url: 'https://res.cloudinary.com/demo/video/upload/black_pants', + md5: '3r43456fdgregregre', + id: 6, + name: 'black_pants', + folder: Folder::fromPath('clothing/pants'), + size: 329987438, + ); + + $this->providerMock + ->expects(self::exactly(4)) + ->method('loadByRemoteId') + ->willReturnCallback( + static fn (string $remoteId): ?RemoteResource => match ($remoteId) { + 'upload|image|blue_sweater' => $blueSweater, + 'upload|image|red_shirt' => $redShirt, + 'upload|image|non_existing_shirt' => throw new RemoteResourceNotFoundException('upload|image|non_existing_shirt'), + 'upload|video|black_pants' => $blackPants, + default => throw new RuntimeException( + sprintf( + 'Failed asserting that argument #1 for method "loadByRemoteId" with value "%s" matches one of the expecting values.', + $remoteId, + ), + ), + }, + ); + + $this->providerMock + ->expects(self::exactly(3)) + ->method('store') + ->willReturnCallback( + static fn (RemoteResource $resource): ?RemoteResource => match ($resource->getRemoteId() . $resource->getFolder()->getPath() . $resource->getName()) { + 'upload|image|blue_sweaterclothing_saleblue_sweater', 'upload|image|red_shirtold_shirtsred shirt', 'upload|video|black_pantsclothing_sale/pantsBlack pants' => $resource, + default => throw new RuntimeException( + sprintf( + 'Failed asserting that argument #1 for method "store" with value "%s" matches one of the expecting values.', + $resource->getRemoteId() . $resource->getFolder()->getPath() . $resource->getName(), + ), + ), + }, + ); + + $response = $this->dynamicFolderModeController->__invoke($request); + + self::assertInstanceOf( + JsonResponse::class, + $response, + ); + + self::assertSame( + '"Notification handled."', + $response->getContent(), + ); + + self::assertSame( + Response::HTTP_OK, + $response->getStatusCode(), + ); + } + + public function testDisplayNameChangedFixed(): void + { + $body = json_encode([ + 'notification_type' => 'resource_display_name_changed', + 'resources' => [ + 'sample' => [ + 'resource_type' => 'image', + 'type' => 'upload', + 'public_id' => 'upload', + 'from_asset_folder' => 'clothing', + 'to_asset_folder' => 'clothing_sale', + 'display_name' => 'blue_sweater', + ], + ], + ]); + + $request = new Request( + [], + [], + [], + [], + [], + [], + $body, + ); + + $request->headers->add( + [ + 'x-cld-timestamp' => time(), + 'x-cld-signature' => 'test', + ], + ); + + $this->signatureVerifierMock + ->expects(self::once()) + ->method('verify') + ->with($request) + ->willReturn(true); + + $event = new NotificationReceivedEvent($request); + + $this->eventDispatcherMock + ->expects(self::once()) + ->method('dispatch') + ->with($event, $event::NAME); + + $this->gatewayMock + ->expects(self::never()) + ->method('invalidateResourceListCache'); + + $this->gatewayMock + ->expects(self::never()) + ->method('invalidateResourceCache'); + + $this->providerMock + ->expects(self::never()) + ->method('loadByRemoteId'); + + $this->providerMock + ->expects(self::never()) + ->method('store'); + + $response = $this->fixedFolderModeController->__invoke($request); + + self::assertInstanceOf( + JsonResponse::class, + $response, + ); + + self::assertSame( + '"Notification handled."', + $response->getContent(), + ); + + self::assertSame( + Response::HTTP_OK, + $response->getStatusCode(), + ); + } + + public function testDisplayNameChangedDynamic(): void + { + $body = json_encode([ + 'notification_type' => 'resource_display_name_changed', + 'resources' => [ + 'blue_sweater' => [ + 'resource_type' => 'image', + 'type' => 'upload', + 'public_id' => 'blue_sweater', + 'new_display_name' => 'blue_sweater', + ], + 'red_shirt' => [ + 'resource_type' => 'image', + 'type' => 'upload', + 'public_id' => 'red_shirt', + 'new_display_name' => 'red shirt', + ], + 'non_existing_shirt' => [ + 'resource_type' => 'image', + 'type' => 'upload', + 'public_id' => 'non_existing_shirt', + 'new_display_name' => 'some shirt', + ], + 'black_pants' => [ + 'resource_type' => 'video', + 'type' => 'upload', + 'public_id' => 'black_pants', + 'new_display_name' => 'Black pants', + ], + ], + ]); + + $request = new Request( + [], + [], + [], + [], + [], + [], + $body, + ); + + $request->headers->add( + [ + 'x-cld-timestamp' => time(), + 'x-cld-signature' => 'test', + ], + ); + + $this->signatureVerifierMock + ->expects(self::once()) + ->method('verify') + ->with($request) + ->willReturn(true); + + $event = new NotificationReceivedEvent($request); + + $this->eventDispatcherMock + ->expects(self::once()) + ->method('dispatch') + ->with($event, $event::NAME); + + $this->gatewayMock + ->expects(self::once()) + ->method('invalidateResourceListCache'); + + $this->gatewayMock + ->expects(self::exactly(4)) + ->method('invalidateResourceCache') + ->willReturnCallback( + static fn (CloudinaryRemoteId $cloudinaryRemoteId) => match ($cloudinaryRemoteId->getRemoteId()) { + 'upload|image|blue_sweater', 'upload|image|red_shirt', 'upload|image|non_existing_shirt', 'upload|video|black_pants' => null, + default => throw new RuntimeException( + sprintf( + 'Failed asserting that argument #1 for method "invalidateResourceCache" with value "%s" matches one of the expecting values.', + $cloudinaryRemoteId->getRemoteId(), + ), + ), + }, + ); + + $blueSweater = new RemoteResource( + remoteId: 'upload|image|blue_sweater', + type: 'image', + url: 'https://res.cloudinary.com/demo/image/upload/blue_sweater', + md5: 'r43tr4t45454324342', + id: 5, + name: 'Sweater (blue)', + folder: Folder::fromPath('clothing'), + size: 380250, + ); + + $redShirt = new RemoteResource( + remoteId: 'upload|image|red_shirt', + type: 'image', + url: 'https://res.cloudinary.com/demo/video/image/red_shirt', + md5: '3r43456fdgregregre', + id: 6, + name: 'red shirt', + folder: Folder::fromPath('shirts'), + size: 3802350, + ); + + $blackPants = new RemoteResource( + remoteId: 'upload|video|black_pants', + type: 'video', + url: 'https://res.cloudinary.com/demo/video/upload/black_pants', + md5: '3r43456fdgregregre', + id: 6, + name: 'black_pants', + folder: Folder::fromPath('clothing/pants'), + size: 329987438, + ); + + $this->providerMock + ->expects(self::exactly(4)) + ->method('loadByRemoteId') + ->willReturnCallback( + static fn (string $remoteId): ?RemoteResource => match ($remoteId) { + 'upload|image|blue_sweater' => $blueSweater, + 'upload|image|red_shirt' => $redShirt, + 'upload|image|non_existing_shirt' => throw new RemoteResourceNotFoundException('upload|image|non_existing_shirt'), + 'upload|video|black_pants' => $blackPants, + default => throw new RuntimeException( + sprintf( + 'Failed asserting that argument #1 for method "loadByRemoteId" with value "%s" matches one of the expecting values.', + $remoteId, + ), + ), + }, + ); + + $this->providerMock + ->expects(self::exactly(3)) + ->method('store') + ->willReturnCallback( + static fn (RemoteResource $resource): ?RemoteResource => match ($resource->getRemoteId() . $resource->getName()) { + 'upload|image|blue_sweaterblue_sweater', 'upload|image|red_shirtred shirt', 'upload|video|black_pantsBlack pants' => $resource, + default => throw new RuntimeException( + sprintf( + 'Failed asserting that argument #1 for method "store" with value "%s" matches one of the expecting values.', + $resource->getRemoteId() . $resource->getName(), + ), + ), + }, + ); + + $response = $this->dynamicFolderModeController->__invoke($request); self::assertInstanceOf( JsonResponse::class, @@ -558,6 +1078,7 @@ public function testTagsChanged(): void ], ], ]); + $request = new Request( [], [], @@ -637,7 +1158,12 @@ public function testTagsChanged(): void 'upload|image|sample' => $image, 'upload|video|video_sample' => $video, 'upload|raw|non_existing_sample' => throw new RemoteResourceNotFoundException('upload|raw|non_existing_sample'), - default => null, + default => throw new RuntimeException( + sprintf( + 'Failed asserting that argument #1 for method "loadByRemoteId" with value "%s" matches one of the expecting values.', + $remoteId, + ), + ), }, ); @@ -655,11 +1181,16 @@ public function testTagsChanged(): void ->willReturnCallback( static fn (RemoteResource $resource): ?RemoteResource => match ($resource->getRemoteId()) { 'upload|image|sample', 'upload|video|video_sample' => $resource, - default => null, + default => throw new RuntimeException( + sprintf( + 'Failed asserting that argument #1 for method "store" with value "%s" matches one of the expecting values.', + $resource->getRemoteId(), + ), + ), }, ); - $response = $this->controller->__invoke($request); + $response = $this->fixedFolderModeController->__invoke($request); self::assertInstanceOf( JsonResponse::class, @@ -718,7 +1249,7 @@ public function testFolderCreated(): void ->expects(self::once()) ->method('invalidateFoldersCache'); - $response = $this->controller->__invoke($request); + $response = $this->fixedFolderModeController->__invoke($request); self::assertInstanceOf( JsonResponse::class, @@ -777,7 +1308,66 @@ public function testFolderDeleted(): void ->expects(self::once()) ->method('invalidateFoldersCache'); - $response = $this->controller->__invoke($request); + $response = $this->fixedFolderModeController->__invoke($request); + + self::assertInstanceOf( + JsonResponse::class, + $response, + ); + + self::assertSame( + '"Notification handled."', + $response->getContent(), + ); + + self::assertSame( + Response::HTTP_OK, + $response->getStatusCode(), + ); + } + + public function testFolderRenamed(): void + { + $body = json_encode([ + 'notification_type' => 'move_or_rename_asset_folder', + 'resources' => [], + ]); + + $request = new Request( + [], + [], + [], + [], + [], + [], + $body, + ); + + $request->headers->add( + [ + 'x-cld-timestamp' => time(), + 'x-cld-signature' => 'test', + ], + ); + + $this->signatureVerifierMock + ->expects(self::once()) + ->method('verify') + ->with($request) + ->willReturn(true); + + $event = new NotificationReceivedEvent($request); + + $this->eventDispatcherMock + ->expects(self::once()) + ->method('dispatch') + ->with($event, $event::NAME); + + $this->gatewayMock + ->expects(self::once()) + ->method('invalidateFoldersCache'); + + $response = $this->fixedFolderModeController->__invoke($request); self::assertInstanceOf( JsonResponse::class, From 1241935f88362aa66fd54daae732e896feba7366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20=C4=8Cupi=C4=87?= Date: Fri, 20 Sep 2024 14:19:56 +0200 Subject: [PATCH 09/13] Fix wrong upload option --- lib/Core/Provider/Cloudinary/Resolver/UploadOptions.php | 2 +- .../Core/Provider/Cloudinary/Resolver/UploadOptionsTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Core/Provider/Cloudinary/Resolver/UploadOptions.php b/lib/Core/Provider/Cloudinary/Resolver/UploadOptions.php index 23833911..50cd57d1 100644 --- a/lib/Core/Provider/Cloudinary/Resolver/UploadOptions.php +++ b/lib/Core/Provider/Cloudinary/Resolver/UploadOptions.php @@ -68,7 +68,7 @@ public function resolve(ResourceStruct $resourceStruct): array ]; if ($resourceStruct->getFolder() && $this->folderMode === CloudinaryProvider::FOLDER_MODE_DYNAMIC) { - $options['folder'] = $resourceStruct->getFolder()->getPath(); + $options['asset_folder'] = $resourceStruct->getFolder()->getPath(); } return $options; diff --git a/tests/lib/Core/Provider/Cloudinary/Resolver/UploadOptionsTest.php b/tests/lib/Core/Provider/Cloudinary/Resolver/UploadOptionsTest.php index 1ec96f6a..153e0754 100644 --- a/tests/lib/Core/Provider/Cloudinary/Resolver/UploadOptionsTest.php +++ b/tests/lib/Core/Provider/Cloudinary/Resolver/UploadOptionsTest.php @@ -193,7 +193,7 @@ public static function dataProvider(): array 'access_mode' => 'authenticated', 'access_control' => [['access_type' => 'token']], 'tags' => ['backup'], - 'folder' => 'files/backups', + 'asset_folder' => 'files/backups', ], CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], @@ -309,7 +309,7 @@ public static function dataProvider(): array 'access_mode' => 'authenticated', 'access_control' => [['access_type' => 'token']], 'tags' => [], - 'folder' => 'videos', + 'asset_folder' => 'videos', ], CloudinaryProvider::FOLDER_MODE_DYNAMIC, ], From 9cb894e1a95cd4c7e64867d5e925a5b7c41f712d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20=C4=8Cupi=C4=87?= Date: Mon, 23 Sep 2024 16:39:36 +0200 Subject: [PATCH 10/13] Fix issue with rename notification --- .../Cloudinary/CloudinaryRemoteId.php | 2 +- .../Cloudinary/CloudinaryRemoteIdTest.php | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/Core/Provider/Cloudinary/CloudinaryRemoteId.php b/lib/Core/Provider/Cloudinary/CloudinaryRemoteId.php index 04ac848b..e03ccdbb 100644 --- a/lib/Core/Provider/Cloudinary/CloudinaryRemoteId.php +++ b/lib/Core/Provider/Cloudinary/CloudinaryRemoteId.php @@ -28,7 +28,7 @@ public static function fromCloudinaryData(array $data, string $folderMode = Clou return new self( $data['type'] ?? 'upload', $data['resource_type'] ?? 'image', - $data['public_id'], + $data['public_id'] ?? $data['to_public_id'], $folderMode, ); } diff --git a/tests/lib/Core/Provider/Cloudinary/CloudinaryRemoteIdTest.php b/tests/lib/Core/Provider/Cloudinary/CloudinaryRemoteIdTest.php index 414fe260..73445978 100644 --- a/tests/lib/Core/Provider/Cloudinary/CloudinaryRemoteIdTest.php +++ b/tests/lib/Core/Provider/Cloudinary/CloudinaryRemoteIdTest.php @@ -50,6 +50,45 @@ public function testFromCloudinaryData(): void self::assertNull($remoteId->getFolder()); } + public function testFromCloudinaryDataRenameNotification(): void + { + $data = [ + 'from_public_id' => 'folder/my_test_image.jpg', + 'to_public_id' => 'folder/subfolder/my_test_image_2.jpg', + 'resource_type' => 'image', + 'type' => 'upload', + 'secure_url' => 'https://cloudinary.com/cloudname/upload/image/folder/subfolder/my_test_image_2.jpg', + 'size' => 23456, + ]; + + $remoteId = CloudinaryRemoteId::fromCloudinaryData($data); + + self::assertSame( + 'upload|image|folder/subfolder/my_test_image_2.jpg', + $remoteId->getRemoteId(), + ); + + self::assertSame( + 'folder/subfolder/my_test_image_2.jpg', + $remoteId->getResourceId(), + ); + + self::assertSame( + 'image', + $remoteId->getResourceType(), + ); + + self::assertSame( + 'upload', + $remoteId->getType(), + ); + + self::assertFolderSame( + Folder::fromPath('folder/subfolder'), + $remoteId->getFolder(), + ); + } + public function testFromRemoteId(): void { $remoteId = CloudinaryRemoteId::fromRemoteId('private|video|media/videos/my_test_video.mp4'); From 2229856f434fd4d43c54cf40ed005b583c8f8213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20=C4=8Cupi=C4=87?= Date: Mon, 23 Sep 2024 22:45:51 +0200 Subject: [PATCH 11/13] Add audio as no extension type --- lib/Core/Provider/Cloudinary/Resolver/UploadOptions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Core/Provider/Cloudinary/Resolver/UploadOptions.php b/lib/Core/Provider/Cloudinary/Resolver/UploadOptions.php index 50cd57d1..11edd6e2 100644 --- a/lib/Core/Provider/Cloudinary/Resolver/UploadOptions.php +++ b/lib/Core/Provider/Cloudinary/Resolver/UploadOptions.php @@ -27,7 +27,7 @@ final class UploadOptions public function __construct( private VisibilityTypeConverter $visibilityTypeConverter, private string $folderMode, - private array $noExtensionMimeTypes = ['image', 'video'], + private array $noExtensionMimeTypes = ['image', 'video', 'audio'], private ?MimeTypesInterface $mimeTypes = null ) { $this->mimeTypes = $this->mimeTypes ?? MimeTypes::getDefault(); From 0e495813c5315a703f652377655261051ee755f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20=C4=8Cupi=C4=87?= Date: Wed, 2 Oct 2024 15:22:00 +0200 Subject: [PATCH 12/13] Fix media preview when browsing and selecting resources --- .../Resource/AbstractController.php | 9 ++- bundle/Resources/config/default_settings.yaml | 25 +++++++- bundle/Resources/public/css/remotemedia.css | 2 +- bundle/Resources/public/js/remotemedia.js | 2 +- .../views/app/interactions.html.twig | 11 +++- frontend/src/components/Preview.vue | 3 +- .../Gateway/CloudinaryApiGateway.php | 7 +++ .../bundle/Controller/Resource/BrowseTest.php | 43 ++++++-------- .../bundle/Controller/Resource/UploadTest.php | 58 +++++++++---------- .../Gateway/CloudinaryApiGatewayTest.php | 12 +++- 10 files changed, 106 insertions(+), 66 deletions(-) diff --git a/bundle/Controller/Resource/AbstractController.php b/bundle/Controller/Resource/AbstractController.php index 1d68b52f..e2d0ad62 100644 --- a/bundle/Controller/Resource/AbstractController.php +++ b/bundle/Controller/Resource/AbstractController.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpFoundation\Response; use function is_array; +use function str_starts_with; abstract class AbstractController { @@ -83,11 +84,17 @@ private function resolveImageUrl(RemoteResource $resource, string $variationName $variationName .= '_protected'; } + if ($resource->getType() === RemoteResource::TYPE_IMAGE) { + $variationName .= '_image'; + } + $location = new RemoteResourceLocation($resource); return match ($resource->getType()) { RemoteResource::TYPE_IMAGE => $this->provider->buildVariation($location, 'ngrm_interface', $variationName)->getUrl(), - RemoteResource::TYPE_VIDEO => $this->provider->buildVideoThumbnailVariation($location, 'ngrm_interface', $variationName)->getUrl(), + RemoteResource::TYPE_VIDEO => str_starts_with($variationName, 'preview') + ? $this->provider->buildVariation($location, 'ngrm_interface', $variationName)->getUrl() + : $this->provider->buildVideoThumbnailVariation($location, 'ngrm_interface', $variationName)->getUrl(), default => '', }; } diff --git a/bundle/Resources/config/default_settings.yaml b/bundle/Resources/config/default_settings.yaml index 0d47745a..5555daf6 100644 --- a/bundle/Resources/config/default_settings.yaml +++ b/bundle/Resources/config/default_settings.yaml @@ -20,20 +20,43 @@ image_variations: transformations: - { name: limit, params: [500, 500] } - { name: quality, params: ['auto', 'eco'] } + preview_image: + transformations: + - { name: limit, params: [500, 500] } + - { name: quality, params: ['auto', 'eco'] } + - { name: format, params: ['jpg'] } preview_protected: transformations: - { name: limit, params: [500, 500] } - { name: quality, params: ['auto', 'eco'] } - { name: effect, params: ['pixelate', 4] } + preview_protected_image: + transformations: + - { name: limit, params: [500, 500] } + - { name: quality, params: ['auto', 'eco'] } + - { name: format, params: ['jpg'] } + - { name: effect, params: ['pixelate', 4] } browse: transformations: - { name: crop, params: [174, 100] } - { name: fill, params: [174, 100] } - { name: quality, params: ['auto', 'eco'] } - + browse_image: + transformations: + - { name: crop, params: [174, 100] } + - { name: fill, params: [174, 100] } + - { name: quality, params: ['auto', 'eco'] } + - { name: format, params: ['jpg'] } browse_protected: transformations: - { name: crop, params: [174, 100] } - { name: fill, params: [174, 100] } - { name: quality, params: ['auto', 'eco'] } - { name: effect, params: ['pixelate', 1] } + browse_protected_image: + transformations: + - { name: crop, params: [174, 100] } + - { name: fill, params: [174, 100] } + - { name: quality, params: ['auto', 'eco'] } + - { name: format, params: ['jpg'] } + - { name: effect, params: ['pixelate', 1] } diff --git a/bundle/Resources/public/css/remotemedia.css b/bundle/Resources/public/css/remotemedia.css index 36de3cf5..15577665 100644 --- a/bundle/Resources/public/css/remotemedia.css +++ b/bundle/Resources/public/css/remotemedia.css @@ -1 +1 @@ -@import url(https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css);@font-face{font-family:ngri;src:url("data:application/x-font-ttf;charset=utf-8;base64,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AQECAgQCAQQCAQIBAQMBAgQDCRUMDBkODR0ODx4PAwgEAQQCAgMCBAcEAwgDAgMCAgMCBAYEAwcDAgMBAQIBAgEDBgMCAgIBAwIDBQMDBQIBASUaAQEDBgICAgIBBAECBAIFCQUFCwUDBgMCBgQBAwIBAwIDBwMBAgECAQEEAQQHAwQHAwIDAgIDAgQHBAMHBAIDAgIDAgQHBA4cDQ0ZCwwUCgkPBwEDAgEBAQICAgEBAwEBAQEBAgECAQEBAQEBARslAQEBAQEBAQECAQEBAQEBAQEBAQEBAgEBAQEBAQEDAQEDAgEBAQECAgQCAQQCAQIBAQMBAgQDCRUMDBkODR0ODx4PAwgEAQQCAgMCBAcEAwgDAgMCAgMCBAYEAwcDAgMBAQIBAgEDBgMCAgIBAwIDBQMDBQIBAQEmGgEBAwUDAQMCAQMCAgQCBAoFBQoGAwUDAwYDAgMCAQMCAwcDAQEBAQIBAQQBBAYEAwcEAgMCAgMCBAcDBAcEAgMCAgMCBAcDDxsODRkLDBQJCRAHAQMCAgECAQMBAQIBAQEBAQECAQEBAQEBAQEBARokSgIBAQEBAQEBAgEBAQEBAQECAQEDAgIBAgIDAgEEAgEBAQECAQIEAggTCgsWDAwZDQwaDQQGAwIDAgEDAgMGBAMGAwIDAQIDAQMGAwMFAwICAgEBAQIDBQMBAwEBAwECBQMCBAMECAQEBgMBAwECAwECAwEDBQIBARcjAwEBAgUDAgYCAgICAQMBAwYCAQIBAQECAwEDBgMDBgMCAwECAwEDBwMDBgMCAwECAwIDBgMMGAwLFQoKEQgIDQUCAgEBAQECAQIBAQIBAQEBAgEBAQEBAQEBAQEBHhYBAQEBAQEBAQEBAgEBAQEBAQMBAQMBAQEBAQEBAgMBAgQBAQIBAQIBAgQCCBMKCxYMDBkMDRoNAwcDAgMBAgMCAwYDBAYDAQMCAQMCAwYDAwUDAgICAgEBAQMFAwEDAQEDAQIFAgMEAwQIBAMHAwEDAQIDAQIDAQIFAwEBFyIEAgUDAgYCAgICAQMBAwYCAQIBAQECAwEDBgMDBgMCAwECAwEDBwMDBgMCAwECAwIDBgMMGAwLFQoKEQgIDQUCAgEBAQECAQIBAQIBAQEBAgEBAQEBAQEBAQEBHxYBvgMFAwICAgEDAgIEAgUJBQUKBgMGAgMGAwIDAgEDAgMHAwEBAQECAQEEAQQHAwMHBAIDAgIDAgQHAwQHBAIDAgIDAgQHBA4cDQ0ZCwwUCgkPBwEDAgIBAgICAQECAQEBAQEBAgEBAQEBAQEBAQEaJQEBAQEBAQEBAgEBAQEBAQEBAQEBAQIBAQEBAQEBAwEBAwIBAQEBAgIEAgEEAgECAQEDAQIEAwkVDAwZDg0dDg8eDwMIBAEEAgIDAgQHBAMIAwIDAgIDAgQGBAMHAwIDAQECAQIBAwYDAgICAQMCAwUDAwUCAQEmGgEBAwUDAgICAQMCAgQCBQkFBQoGAwYCAwYDAgMCAQMCAwcDAQEBAQIBAQQBBAcDAwcEAgMCAgMCBAcDBAcEAgMCAgMCBAcEDhwNDRkLDBQKCQ8HAQMCAgECAgIBAQIBAQEBAQECAQEBAQEBAQEBARolAQEBAQEBAQECAQEBAQEBAQEBAQEBAgEBAQEBAQEDAQEDAgEBAQECAgQCAQQCAQIBAQMBAgQDCRUMDBkODR0ODx4PAwgEAQQCAgMCBAcEAwgDAgMCAgMCBAYEAwcDAgMBAQIBAgEDBgMCAgIBAwIDBQMDBQIBAQElGgEBQQECBQMCBgICAgIBAwEDBgIBAgEBAQIDAQMGAwMGAwIDAQIDAQMHAwMGAwIDAQIDAgMGAwwYDAsVCgoRCAgNBQICAQEBAQIBAgEBAgEBAQECAQEBAQEBAQEBAQEeFwEBAQEBAQEBAQECAQEBAQECAgEBAwEBAgEBAQIDAgEEAgIBAQIBAgQCCBMKCxYMDBkNDBoNBAYDAgMCAQMCAwYEAwYDAQMCAQMCAwYDAwUDAgICAgECAwUDAQMBAQMBAgUDAgQDBAgEAwcDAQMBAgMBAgMBAgUDAQEXIgQBAgUDAgYCAgICAQMBAwYCAQIBAQECAwEDBgMDBgMCAwECAwEDBwMDBgMCAwECAwIDBgMMGAwLFQoKEQgIDQUCAgEBAQECAQIBAQIBAQEBAgEBAQEBAQEBAQEBHhYBAQEBAQEBAQECAQEBAQECAgEBAwEBAgEBAQIDAgEEAgIBAQIBAgQCCBMKCxYMDBkNDBoNBAYDAgMCAQMCAwYDBAYDAQMCAQMCAwYDAwUDAgICAgECAwUDAQMBAQMBAgUDAgQDBAgEAwcDAQMBAgMBAgMBAgUDAQEXIwMAAAAAAQAC/8ID/gO+AFMAACU4ATEJATgBMT4BNzYmLwEuAQcOAQc4ATEJATgBMS4BJyYGDwEOARceARc4ATEJATgBMQ4BBwYWHwEeATc+ATc4ATEJATgBMR4BFxY2PwE+AScuAQP3/skBNwIEAQMDB5MHEgkDBgL+yf7JAgYDCRIHkwcDAwEEAgE3/skCBAEDAweTBxIJAwYCATcBNwIGAwkSB5MHAwMBBIkBNwE3AgYDCRIHkwcDAwEEAv7JATcCBAEDAweTBxIJAwYC/sn+yQIGAwkSB5MHAwMBBAIBN/7JAgQBAwMHkwcSCQMGAAABAAAAgAQAAsAAKgAAATQmJyYnLgEnJiMiBgcuASMiBhUUFhUuASMiBw4BBwYVFBceARcWMyEyNgQATDkBExNBKyoxOWEhEjcgOE4BCBEJKCQjNQ8PDw81IyQoApBIZgEuPl4OMCorPxISMSoYHE43BQoEAQIQDzQkIygoJCM1Dw9mAAAEAAAAQAQAA0AACwAXACsALwAAATQ2MzIWFRQGIyImJTQ2MzIWFRQGIyImBTU0JiMhIgYVERQWMyEyNj0BBREBITUhAYBeQkJeXkJCXv6AXkJCXl5CQl4DACYa/YAaJiYaAoAaJgEA/oD+AAIAAqBCXl5CQl5eQkJeXkJCXl7+YBomJhr+wBomJhpgoAHA/sDAAAAAAgBA/8ADwAPAABQAJgAAAREhIiY1NDYzIREhIgYVERQWMyERATE4ATEiBhUUFjM4ATkBITUhA4D9YCg4OCgCYP2ANUtLNQMA/SANExMNAmD9oANA/MA4KCg4AwBLNf0ANUsDgP1AEw0NE0AAAQAAAAEAACKXau1fDzz1AAsEAAAAAADWNiSXAAAAANY2JJcAAP/ABAADwAAAAAgAAgAAAAAAAAABAAADwP/AAAAEAAAAAAAEAAABAAAAAAAAAAAAAAAAAAAACgQAAAAAAAAAAAAAAAIAAAAEAAAOBAAAAAQAAAIEAAAABAAAAAQAAEAAAAAAAAoAFAAeAGoGPga0BvYHQAd2AAAAAQAAAAoEhwAEAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAQAAAABAAAAAAACAAcARQABAAAAAAADAAQALQABAAAAAAAEAAQAWgABAAAAAAAFAAsADAABAAAAAAAGAAQAOQABAAAAAAAKABoAZgADAAEECQABAAgABAADAAEECQACAA4ATAADAAEECQADAAgAMQADAAEECQAEAAgAXgADAAEECQAFABYAFwADAAEECQAGAAgAPQADAAEECQAKADQAgG5ncmkAbgBnAHIAaVZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMG5ncmkAbgBnAHIAaW5ncmkAbgBnAHIAaVJlZ3VsYXIAUgBlAGcAdQBsAGEAcm5ncmkAbgBnAHIAaUZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") format("truetype");font-weight:400;font-style:normal}@font-face{font-family:fa-ngrm;src:url(../fonts/fa-ngrm.eot);src:url(../fonts/fa-ngrm.eot#iefix) format("embedded-opentype"),url(data:font/woff2;base64,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) format("woff2"),url(data:font/woff;base64,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) format("woff"),url(../fonts/fa-ngrm.ttf) format("truetype"),url(../img/fa-ngrm.svg#fa-ngrm) format("svg");font-weight:400;font-style:normal}[class*=" ngrm-icon-"]:before,[class^=ngrm-icon-]:before{font-family:fa-ngrm;font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;margin-right:.2em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;margin-left:.2em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.ngrm-icon-cancel:before{content:"\E800"}.ngrm-icon-floppy:before{content:"\E801"}.ngrm-icon-ok:before{content:"\E802"}.ngrm-icon-ccw:before{content:"\E803"}.ngrm-icon-folder:before{content:"\E804"}.ngrm-icon-upload:before{content:"\E805"}.ngrm-icon-play:before{content:"\E811"}.ngrm-icon-trash:before{content:"\F1F8"}.ngrm-icon-file-archive-o:before{content:"\F1C6"}.v-select{position:relative;font-family:inherit}.v-select,.v-select *{-webkit-box-sizing:border-box;box-sizing:border-box}@-webkit-keyframes vSelectSpinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes vSelectSpinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.vs__fade-enter-active,.vs__fade-leave-active{-webkit-transition:opacity .15s cubic-bezier(1,.5,.8,1);transition:opacity .15s cubic-bezier(1,.5,.8,1)}.vs__fade-enter,.vs__fade-leave-to{opacity:0}.vs--disabled .vs__clear,.vs--disabled .vs__dropdown-toggle,.vs--disabled .vs__open-indicator,.vs--disabled .vs__search,.vs--disabled .vs__selected{cursor:not-allowed;background-color:#f8f8f8}.v-select[dir=rtl] .vs__actions{padding:0 3px 0 6px}.v-select[dir=rtl] .vs__clear{margin-left:6px;margin-right:0}.v-select[dir=rtl] .vs__deselect{margin-left:0;margin-right:2px}.v-select[dir=rtl] .vs__dropdown-menu{text-align:right}.vs__dropdown-toggle{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0 0 4px 0;background:none;border:1px solid rgba(60,60,60,.26);border-radius:4px;white-space:normal}.vs__dropdown-toggle,.vs__selected-options{display:-webkit-box;display:-ms-flexbox;display:flex}.vs__selected-options{-ms-flex-preferred-size:100%;flex-basis:100%;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:0 2px;position:relative}.vs__actions{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:4px 6px 0 3px}.vs--searchable .vs__dropdown-toggle{cursor:text}.vs--unsearchable .vs__dropdown-toggle{cursor:pointer}.vs--open .vs__dropdown-toggle{border-bottom-color:transparent;border-bottom-left-radius:0;border-bottom-right-radius:0}.vs__open-indicator{fill:rgba(60,60,60,.5);-webkit-transform:scale(1);transform:scale(1);-webkit-transition:-webkit-transform .15s cubic-bezier(1,-.115,.975,.855);transition:-webkit-transform .15s cubic-bezier(1,-.115,.975,.855);transition:transform .15s cubic-bezier(1,-.115,.975,.855);transition:transform .15s cubic-bezier(1,-.115,.975,.855),-webkit-transform .15s cubic-bezier(1,-.115,.975,.855);-webkit-transition-timing-function:cubic-bezier(1,-.115,.975,.855);transition-timing-function:cubic-bezier(1,-.115,.975,.855)}.vs--open .vs__open-indicator{-webkit-transform:rotate(180deg) scale(1);transform:rotate(180deg) scale(1)}.vs--loading .vs__open-indicator{opacity:0}.vs__clear{fill:rgba(60,60,60,.5);padding:0;border:0;background-color:transparent;cursor:pointer;margin-right:8px}.vs__dropdown-menu{display:block;position:absolute;top:calc(100% - 1px);left:0;z-index:1000;padding:5px 0;margin:0;width:100%;max-height:350px;min-width:160px;overflow-y:auto;-webkit-box-shadow:0 3px 6px 0 rgba(0,0,0,.15);box-shadow:0 3px 6px 0 rgba(0,0,0,.15);border:1px solid rgba(60,60,60,.26);border-top-style:none;border-radius:0 0 4px 4px;text-align:left;list-style:none;background:#fff}.vs__no-options{text-align:center}.vs__dropdown-option{line-height:1.42857143;display:block;padding:3px 20px;clear:both;color:#333;white-space:nowrap}.vs__dropdown-option:hover{cursor:pointer}.vs__dropdown-option--highlight{background:#5897fb;color:#fff}.vs__selected{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;background-color:#f0f0f0;border:1px solid rgba(60,60,60,.26);border-radius:4px;color:#333;line-height:1.4;margin:4px 2px 0 2px;padding:0 .25em}.vs__deselect{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-appearance:none;-moz-appearance:none;appearance:none;margin-left:4px;padding:0;border:0;cursor:pointer;background:none;fill:rgba(60,60,60,.5);text-shadow:0 1px 0 #fff}.vs--single .vs__selected{background-color:transparent;border-color:transparent}.vs--single.vs--open .vs__selected{position:absolute;opacity:.4}.vs--single.vs--searching .vs__selected{display:none}.vs__search::-ms-clear,.vs__search::-webkit-search-cancel-button,.vs__search::-webkit-search-decoration,.vs__search::-webkit-search-results-button,.vs__search::-webkit-search-results-decoration{display:none}.vs__search,.vs__search:focus{-webkit-appearance:none;-moz-appearance:none;appearance:none;line-height:1.4;font-size:1em;border:1px solid transparent;border-left:none;outline:none;margin:4px 0 0 0;padding:0 7px;background:none;-webkit-box-shadow:none;box-shadow:none;width:0;max-width:100%;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.vs__search::-webkit-input-placeholder{color:inherit}.vs__search::-moz-placeholder{color:inherit}.vs__search:-ms-input-placeholder{color:inherit}.vs__search::-ms-input-placeholder{color:inherit}.vs__search::placeholder{color:inherit}.vs--unsearchable .vs__search{opacity:1}.vs--unsearchable .vs__search:hover{cursor:pointer}.vs--single.vs--searching:not(.vs--open):not(.vs--loading) .vs__search{opacity:.2}.vs__spinner{-ms-flex-item-align:center;align-self:center;opacity:0;font-size:5px;text-indent:-9999em;overflow:hidden;border-top:.9em solid hsla(0,0%,39.2%,.1);border-right:.9em solid hsla(0,0%,39.2%,.1);border-bottom:.9em solid hsla(0,0%,39.2%,.1);border-left:.9em solid rgba(60,60,60,.45);-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-animation:vSelectSpinner 1.1s linear infinite;animation:vSelectSpinner 1.1s linear infinite;-webkit-transition:opacity .1s;transition:opacity .1s}.vs__spinner,.vs__spinner:after{border-radius:50%;width:5em;height:5em}.vs--loading .vs__spinner{opacity:1}.ngremotemedia-select-folder-container,.ngrm-model-portal+form,.ngrm-model-portal .ngrm-overlay{-webkit-box-sizing:border-box;box-sizing:border-box;font-family:Roboto,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#333}.ngremotemedia-select-folder-container *,.ngremotemedia-select-folder-container :after,.ngremotemedia-select-folder-container :before,.ngrm-model-portal+form *,.ngrm-model-portal+form :after,.ngrm-model-portal+form :before,.ngrm-model-portal .ngrm-overlay *,.ngrm-model-portal .ngrm-overlay :after,.ngrm-model-portal .ngrm-overlay :before{-webkit-box-sizing:inherit;box-sizing:inherit}.ngremotemedia-select-folder-container .btn,.ngrm-model-portal+form .btn,.ngrm-model-portal .ngrm-overlay .btn{border-radius:4px;text-align:center;font-size:14px;line-height:18px;color:#fff;padding:8px 16px;border:1px solid #e4e4e4;background:#fff;color:#333}.ngremotemedia-select-folder-container .btn:active,.ngrm-model-portal+form .btn:active,.ngrm-model-portal .ngrm-overlay .btn:active{background:#d7d7d7}.ngremotemedia-select-folder-container .btn.btn-blue,.ngrm-model-portal+form .btn.btn-blue,.ngrm-model-portal .ngrm-overlay .btn.btn-blue{background:#009ac7;border:0;color:#fff}.ngremotemedia-select-folder-container .btn.btn-blue:active,.ngrm-model-portal+form .btn.btn-blue:active,.ngrm-model-portal .ngrm-overlay .btn.btn-blue:active{background:#007394}.ngremotemedia-select-folder-container .v-select,.ngrm-model-portal+form .v-select,.ngrm-model-portal .ngrm-overlay .v-select{font-size:14px;line-height:16px;background-color:#fff}.ngremotemedia-select-folder-container .v-select .vs__dropdown-toggle,.ngrm-model-portal+form .v-select .vs__dropdown-toggle,.ngrm-model-portal .ngrm-overlay .v-select .vs__dropdown-toggle{border-radius:0;border:1px solid #e4e4e4;padding:3px 5px 6px}.ngremotemedia-select-folder-container .v-select input::-webkit-input-placeholder,.ngrm-model-portal+form .v-select input::-webkit-input-placeholder,.ngrm-model-portal .ngrm-overlay .v-select input::-webkit-input-placeholder{color:#333;font-style:italic}.ngremotemedia-select-folder-container .v-select input::-moz-placeholder,.ngrm-model-portal+form .v-select input::-moz-placeholder,.ngrm-model-portal .ngrm-overlay .v-select input::-moz-placeholder{color:#333;font-style:italic}.ngremotemedia-select-folder-container .v-select input:-ms-input-placeholder,.ngrm-model-portal+form .v-select input:-ms-input-placeholder,.ngrm-model-portal .ngrm-overlay .v-select input:-ms-input-placeholder{color:#333;font-style:italic}.ngremotemedia-select-folder-container .v-select input::-ms-input-placeholder,.ngrm-model-portal+form .v-select input::-ms-input-placeholder,.ngrm-model-portal .ngrm-overlay .v-select input::-ms-input-placeholder{color:#333;font-style:italic}.ngremotemedia-select-folder-container .v-select input::placeholder,.ngrm-model-portal+form .v-select input::placeholder,.ngrm-model-portal .ngrm-overlay .v-select input::placeholder{color:#333;font-style:italic}.ngremotemedia-select-folder-container ::-webkit-input-placeholder,.ngrm-model-portal+form ::-webkit-input-placeholder,.ngrm-model-portal .ngrm-overlay ::-webkit-input-placeholder{color:#999}.ngremotemedia-select-folder-container :-ms-input-placeholder,.ngrm-model-portal+form :-ms-input-placeholder,.ngrm-model-portal .ngrm-overlay :-ms-input-placeholder{color:#999}.ngremotemedia-select-folder-container ::-moz-placeholder,.ngrm-model-portal+form ::-moz-placeholder,.ngrm-model-portal .ngrm-overlay ::-moz-placeholder{color:#999}.ngremotemedia-select-folder-container ::-ms-input-placeholder,.ngrm-model-portal+form ::-ms-input-placeholder,.ngrm-model-portal .ngrm-overlay ::-ms-input-placeholder{color:#999}.ngremotemedia-select-folder-container ::placeholder,.ngrm-model-portal+form ::placeholder,.ngrm-model-portal .ngrm-overlay ::placeholder{color:#999}.ngrm-model-portal .ngrm-overlay .ng-icon{font-size:4.5em;font-family:ngri;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;text-align:center;line-height:80px;display:block;color:#333}.ngrm-model-portal .ngrm-overlay .ng-icon.big{font-size:64px;color:#333}.ngrm-model-portal .ngrm-overlay .ng-icon.ng-warning:before{content:"\E900"}.ngrm-model-portal .ngrm-overlay .ng-icon.ng-spinner:before{content:"\E901"}.ngrm-model-portal .ngrm-overlay .ng-icon.ng-close:before{content:"\E902"}.ngrm-model-portal .ngrm-overlay .ng-icon.ng-cloud:before{content:"\E903"}.ngrm-model-portal .ngrm-overlay .ng-icon.ng-video:before{content:"\E904"}.ngrm-model-portal .ngrm-overlay .ng-icon.ng-book:before{content:"\E905"}.ngrm-model-portal .ngrm-overlay .image-meta{overflow:visible}.ngrm-model-portal .ngrm-overlay .image-meta h3{font-weight:700}.ngrm-model-portal .ngrm-overlay .image-meta p{color:#737373}.ngrm-model-portal .ngrm-overlay .image-meta input.media-alttext,.ngrm-model-portal .ngrm-overlay .image-meta input.media-caption,.ngrm-model-portal .ngrm-overlay .image-meta input.media-watermarktext{width:100%;border-radius:0;border:1px solid #e4e4e4;padding:7px 10px 7px 10px;margin-bottom:10px}.ngrm-model-portal .ngrm-overlay .image-meta .image-meta-data{margin-top:20px}.ngrm-model-portal .ngrm-overlay .image-wrap:has(.icon-doc){position:relative;height:200px;max-width:400px;display:block;margin-bottom:4px}.ngrm-model-portal .ngrm-overlay .image-wrap:has(.icon-doc):before{position:absolute;content:""}.ngrm-model-portal .ngrm-overlay .image-wrap:has(.icon-doc):before{background-color:rgba(0,0,0,.7);top:0;bottom:0;left:0;right:0}.ngrm-model-portal .ngrm-overlay .image-wrap .icon-doc{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#fff;font-size:70px}.ngrm-model-portal .ngrm-overlay .image-wrap img{max-width:100%}.ngrm-model-portal .ngrm-overlay .vue-treeselect{font-size:14px;line-height:16px}.ngrm-model-portal .ngrm-overlay .vue-treeselect .vue-treeselect__control{border-radius:0;border:1px solid #e4e4e4}.ngrm-model-portal .ngrm-overlay .vue-treeselect .vue-treeselect__placeholder{color:#333;font-style:italic}@-webkit-keyframes spinning{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes spinning{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.ngremotemedia-buttons{margin-top:20px}.ngremotemedia-buttons .ngremotemedia-local-file-container{margin:10px 0 10px 0}.ngremotemedia-buttons .ngremotemedia-local-file.btn.upload-from-disk,.ngremotemedia-buttons input[type=button]{border-radius:4px;text-align:center;font-size:14px!important;line-height:18px;padding:8px 16px;border:1px solid #e4e4e4;background:#f1f4fa;cursor:pointer;color:#333;font-weight:400;font-family:Arial,Helvetica,sans-serif}.ngremotemedia-buttons .ngremotemedia-local-file.btn.upload-from-disk{background:#fff}.o2k7Skin .mceIcon.mce_ngremotemedia img{width:2rem}.ngremotemedia-image .image-wrap .file-placeholder[data-v-2db10e6e]{position:relative;max-width:500px;height:280px;display:block;margin-bottom:4px}.ngremotemedia-image .image-wrap .file-placeholder .icon-doc[data-v-2db10e6e]{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#fff;font-size:40px}.ngremotemedia-image .image-wrap .file-placeholder[data-v-2db10e6e]:before{position:absolute;content:"";background-color:rgba(0,0,0,.7);top:0;bottom:0;left:0;right:0}.mediaFacets[data-v-278bb214]{width:362px;-ms-flex-negative:0;flex-shrink:0;-webkit-box-shadow:inset -1px 0 0 0 #e4e4e4;box-shadow:inset -1px 0 0 0 #e4e4e4}.mediaFacets .body[data-v-278bb214]{-webkit-box-shadow:inset 0 -1px 0 0 #e4e4e4,inset 0 1px 0 0 #e4e4e4,inset -1px 0 0 0 #e4e4e4;box-shadow:inset 0 -1px 0 0 #e4e4e4,inset 0 1px 0 0 #e4e4e4,inset -1px 0 0 0 #e4e4e4;background:#fff;padding:30px 15px}.mediaFacets .body .form-field+.form-field[data-v-278bb214]{margin-top:15px}.mediaFacets .body .form-field label[data-v-278bb214],.mediaFacets .body .search-wrapper .search-label[data-v-278bb214]{font-size:12px;font-weight:700;line-height:18px;color:#757575;margin-bottom:3px;display:block}.mediaFacets .body .search-wrapper[data-v-278bb214]{margin:30px 0 0}.mediaFacets .body .search-wrapper .search[data-v-278bb214]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:5px 0}.mediaFacets .body .search-wrapper .search input[data-v-278bb214],.mediaFacets .body .search-wrapper .search ul[data-v-278bb214]{font-size:14px;line-height:16px}.mediaFacets .body .search-wrapper .search ul[data-v-278bb214]{margin:0;padding:5px;list-style:none;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:1px solid #e4e4e4;min-width:75px;display:none}.mediaFacets .body .search-wrapper .search ul li[data-v-278bb214]{cursor:auto;margin-right:10px;padding:4px 10px;min-width:45px}.mediaFacets .body .search-wrapper .search ul li[data-v-278bb214]:last-child,.mediaFacets .body .search-wrapper .search ul li[data-v-278bb214]:only-child{margin:0}.mediaFacets .body .search-wrapper .search ul li.active[data-v-278bb214]{background:#009ac7;color:#fff;border-radius:4px;-webkit-box-shadow:inset -1px 0 0 0 #d7d7d7,inset 1px 0 0 0 #d7d7d7,inset 0 1px 0 0 #d7d7d7,inset 0 -1px 0 0 #d7d7d7;box-shadow:inset -1px 0 0 0 #d7d7d7,inset 1px 0 0 0 #d7d7d7,inset 0 1px 0 0 #d7d7d7,inset 0 -1px 0 0 #d7d7d7}.mediaFacets .body .search-wrapper .search input[data-v-278bb214]{border:1px solid #e4e4e4;padding:9px 10px;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.mediaFacets .ng-spinner[data-v-278bb214]{position:fixed;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.mediaFacets .ng-spinner[data-v-278bb214]:before{display:inline-block;-webkit-animation:spinning 1.5s linear infinite;animation:spinning 1.5s linear infinite}.media-gallery[data-v-5aa52c90]{position:relative;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.media-gallery .items[data-v-5aa52c90]{padding:15px;overflow-y:auto;height:calc(100% - 50px)}.media-gallery .items.loading[data-v-5aa52c90]{opacity:.5}.media-gallery .items .media[data-v-5aa52c90]{width:190px;min-height:182px;max-height:190px;padding:8px;margin:0 15px 15px 0;background-color:#fff;display:inline-block}.media-gallery .items .media .media-container[data-v-5aa52c90]{width:100%}.media-gallery .items .media .img[data-v-5aa52c90]{display:block;margin-bottom:4px;-o-object-fit:cover;object-fit:cover;height:100px;width:100%;overflow:hidden;text-overflow:ellipsis}.media-gallery .items .media .file-placeholder[data-v-5aa52c90]{position:relative;height:95px;display:block;margin-bottom:4px}.media-gallery .items .media .file-placeholder .icon-doc[data-v-5aa52c90]{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#fff;font-size:40px}.media-gallery .items .media .file-placeholder[data-v-5aa52c90]:before{position:absolute;content:"";background-color:rgba(0,0,0,.7);top:0;bottom:0;left:0;right:0}.media-gallery .items .media .filename[data-v-5aa52c90]{overflow:hidden;display:inline-block;text-overflow:ellipsis;white-space:nowrap;width:100%;text-align:center;font-size:16px;line-height:20px;margin-top:4px;margin-bottom:0}.media-gallery .items .media .size-description[data-v-5aa52c90]{font-size:12px;line-height:14px;text-align:center;color:#999}.media-gallery .items .media .size-description .format[data-v-5aa52c90]{text-transform:uppercase}.media-gallery .items .media.selected[data-v-5aa52c90]{border:1px solid #009ac7}.media-gallery .items .media .select-btn[data-v-5aa52c90]{margin-top:8px;padding:3px;width:100%}.media-gallery .folder-empty[data-v-5aa52c90]{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.media-gallery .folder-empty span[data-v-5aa52c90]{display:block;text-align:center;font-size:14px;line-height:16px}.media-gallery .folder-empty span.ngrm-icon-folder[data-v-5aa52c90]{color:#999;font-size:33px}.media-gallery .folder-empty span strong[data-v-5aa52c90]{display:block;margin:5px 0;font-size:16px;line-height:19px}.media-gallery .load-more-wrapper[data-v-5aa52c90]{padding:8px 15px;background-color:#fff;text-align:right;-webkit-box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;position:absolute;bottom:0;left:0;right:0}.ngrm-overlay[data-v-9d33d07a]{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(0,0,0,.8);z-index:9999}.ngrm-overlay .media-modal[data-v-9d33d07a]{background-color:#f5f5f5;-webkit-box-shadow:0 5px 15px 0 rgba(0,0,0,.5);box-shadow:0 5px 15px 0 rgba(0,0,0,.5);margin:32px;height:calc(100vh - 64px)}.ngrm-overlay .media-modal .title[data-v-9d33d07a]{padding:15px;font-size:16px;font-weight:700;line-height:20px;color:#333;background:#fff;text-transform:uppercase;-webkit-box-shadow:inset 0 -1px 0 0 #e4e4e4;box-shadow:inset 0 -1px 0 0 #e4e4e4}.ngrm-overlay .media-modal .title .close[data-v-9d33d07a]{float:right;cursor:pointer;padding:2px 10px}.ngrm-overlay .media-modal .body[data-v-9d33d07a]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;position:relative;height:calc(100% - 50px)}.ngrm-overlay .img-placeholder img[data-v-9d33d07a]{height:100%}.ngremotemedia-buttons input[type=button]{margin-bottom:4px;margin-right:4px}.help-block.description{margin-right:6px}.btn,.sidebar-crop .buttons{font-size:14px!important;font-family:Arial,Helvetica,sans-serif}.ngremotemedia-tags .vs__selected-options input[type=search].vs__search{border:none}.ngremotemedia-tags select[hidden=hidden]{display:none}.ngremotemedia-image h3.title{word-break:break-word}.ngremotemedia-image .image-wrap img{max-width:100%}.input-file-name-wrapper{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.input-file-name-wrapper .v-select,.input-file-name-wrapper input[type=text]{margin-right:16px}.input-file-name-wrapper .v-select.vs--single.vs--searchable{margin-right:10px}.input-file-name-wrapper .v-select.vs--single.vs--searchable input.vs__search{background:transparent}.input-file-name-wrapper button.btn{margin-left:40%;font-size:14px!important;font-family:Arial}.input-file-name-wrapper .vs__dropdown-toggle{height:37px}.media-gallery .items .media{line-height:normal}.ng-spinner[data-v-751395c8]{position:fixed;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.ng-spinner[data-v-751395c8]:before{display:inline-block;-webkit-animation:spinning 1.5s linear infinite;animation:spinning 1.5s linear infinite}.sidebar-crop[data-v-ad171770]{width:264px;-ms-flex-negative:0;flex-shrink:0;-webkit-box-shadow:inset -1px 0 0 0 #e4e4e4;box-shadow:inset -1px 0 0 0 #e4e4e4}.sidebar-crop .buttons[data-v-ad171770]{background:#fff;padding:15px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-shadow:inset 0 -1px 0 0 #e4e4e4;box-shadow:inset 0 -1px 0 0 #e4e4e4;margin-right:1px}.sidebar-crop .buttons button[data-v-ad171770]{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.sidebar-crop .buttons button.crop-btn-add[data-v-ad171770]{margin-left:10px}.sidebar-crop .buttons button[data-v-ad171770]:only-child{width:100%}.sidebar-crop-label span[data-v-ad171770]{color:#999;font-size:14px;line-height:18px;display:inline-block;padding:31px 15px 15px;width:100%;-webkit-box-shadow:inset 0 -1px 0 0 #e4e4e4;box-shadow:inset 0 -1px 0 0 #e4e4e4}.unselectedVariations[data-v-ad171770]{position:absolute;top:0;left:0;width:264px;height:100%;-webkit-transform:translateX(264px);transform:translateX(264px);background:#fff;-webkit-box-shadow:inset -1px 0 0 0 #e4e4e4;box-shadow:inset -1px 0 0 0 #e4e4e4;z-index:10}.unselectedVariations>div[data-v-ad171770]{padding:15px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;background-color:#fff;-webkit-box-shadow:inset 0 -1px 0 0 #e4e4e4,inset -1px 0 0 0 #e4e4e4;box-shadow:inset 0 -1px 0 0 #e4e4e4,inset -1px 0 0 0 #e4e4e4}.unselectedVariations>div input[data-v-ad171770],.unselectedVariations>div label[data-v-ad171770]{cursor:pointer}.unselectedVariations>div input[data-v-ad171770]{margin-right:11px}.unselectedVariations>div label[data-v-ad171770]{width:100%}.unselectedVariations>div .name[data-v-ad171770]{color:#333;font-size:14px;line-height:18px}.unselectedVariations>div .formatted-size[data-v-ad171770]{color:#999;font-size:12px;line-height:18px;display:block}.unselectedVariations>div.disabled[data-v-ad171770]{-ms-flex-wrap:wrap;flex-wrap:wrap;background-color:#f5f5f5;cursor:auto;color:#ddd;padding:15px 15px 5px}.unselectedVariations>div.disabled input[data-v-ad171770],.unselectedVariations>div.disabled label[data-v-ad171770],.unselectedVariations>div.disabled span[data-v-ad171770]{cursor:auto}.unselectedVariations>div.disabled label[data-v-ad171770]{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;width:auto;color:#999}.unselectedVariations .legend-not-selectable[data-v-ad171770]{width:100%;font-size:.75rem;color:#a41034;display:inline-block;text-align:right}.selectedVariations ul[data-v-ad171770]{list-style:none;padding:0;margin:0}.selectedVariations ul li[data-v-ad171770]{padding:15px 0 15px 15px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-shadow:inset -1px 0 0 0 #e4e4e4,inset 0 -1px 0 0 #e4e4e4;box-shadow:inset -1px 0 0 0 #e4e4e4,inset 0 -1px 0 0 #e4e4e4;background-color:#fff;cursor:pointer}.selectedVariations ul li.disabled[data-v-ad171770]{background-color:#f5f5f5;cursor:auto}.selectedVariations ul li.selected.set[data-v-ad171770]{color:#90ee90}.selectedVariations ul li span[data-v-ad171770]{display:block;color:#333;font-size:14px;line-height:18px}.selectedVariations ul li a[data-v-ad171770]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.selectedVariations ul li a span[data-v-ad171770]{padding:5px}.selectedVariations ul li a .ngrm-icon-trash[data-v-ad171770]{color:#009ac7;padding:10px}.selectedVariations ul li .formatted-size[data-v-ad171770]{color:#999;font-size:12px;line-height:18px}.selectedVariations ul li .circle-orange[data-v-ad171770]{width:8px;height:8px;background-color:orange;border-radius:50%}.selectedVariations .set .circle-orange[data-v-ad171770]{display:none}.crop .cropper[data-v-5f73791e]{position:relative;margin:0 auto}.crop .cropper button[data-v-5f73791e]{margin-left:8px}.crop .cropper .buttons[data-v-5f73791e]{position:absolute}.crop .preview[data-v-5f73791e]{width:100%;height:500px;overflow:hidden}.crop-container[data-v-45497070]{overflow-y:auto;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;margin:30px 30px 80px}.crop-container[data-v-45497070]:empty{display:none}.img-placeholder[data-v-45497070]{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;padding:60px 60px 110px}.img-placeholder img[data-v-45497070]{max-width:100%;height:auto;margin:0 auto;display:block}.action-strip[data-v-45497070]{padding:8px 15px;background-color:#fff;text-align:right;-webkit-box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;position:absolute;bottom:0;left:264px;right:0}.action-strip button[data-v-45497070]{margin-left:10px}.action-strip .ngrm-icon-floppy[data-v-45497070]{margin-right:5px}.folder-gallery[data-v-c53732c8]{position:relative;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;height:calc(100% - 50px);overflow-y:auto}.folder-gallery .items[data-v-c53732c8]{padding:15px}.folder-gallery .items.loading[data-v-c53732c8]{opacity:.5}.folder-gallery .items .breadcrumbs[data-v-c53732c8]{background-color:#fff;width:100%;margin-bottom:20px;padding:10px}.folder-gallery .items .breadcrumbs a[data-v-c53732c8]{color:#009ac7}.folder-gallery .items .info[data-v-c53732c8]{font-style:italic;margin-bottom:10px;margin-left:10px}.folder-gallery .items .media[data-v-c53732c8]{width:177px;min-height:182px;max-height:190px;padding:8px;margin:0 15px 15px 0;background-color:#fff;display:inline-block}.folder-gallery .items .media .media-container[data-v-c53732c8]{width:100%}.folder-gallery .items .media .img[data-v-c53732c8]{display:block;margin-bottom:4px;-o-object-fit:cover;object-fit:cover;height:92px;width:100%;overflow:hidden;text-overflow:ellipsis}.folder-gallery .items .media .file-placeholder[data-v-c53732c8]{position:relative;height:92px;display:block;margin-bottom:4px}.folder-gallery .items .media .file-placeholder .icon-doc[data-v-c53732c8]{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#fff;font-size:40px}.folder-gallery .items .media .file-placeholder[data-v-c53732c8]:before{position:absolute;content:"";background-color:rgba(0,0,0,.7);top:0;bottom:0;left:0;right:0}.folder-gallery .items .media.new-folder input[data-v-c53732c8]{width:100%;margin-top:5px}.folder-gallery .items .media.new-folder .select-btn[data-v-c53732c8]{background:#2e8b57}.folder-gallery .items .media.new-folder .file-placeholder[data-v-c53732c8]:before{background-color:rgba(0,0,0,.2);top:0;bottom:0;left:0;right:0}.folder-gallery .items .media .filename[data-v-c53732c8]{overflow:hidden;display:inline-block;text-overflow:ellipsis;white-space:nowrap;width:100%;text-align:center;font-size:16px;line-height:20px;margin-top:4px;margin-bottom:0}.folder-gallery .items .media .size-description[data-v-c53732c8]{font-size:12px;line-height:14px;text-align:center;color:#999}.folder-gallery .items .media .size-description .format[data-v-c53732c8]{text-transform:uppercase}.folder-gallery .items .media.selected[data-v-c53732c8]{border:1px solid #009ac7}.folder-gallery .items .media .select-btn[data-v-c53732c8]{margin-top:10px;padding:3px;width:100%}.folder-gallery .folder-empty[data-v-c53732c8]{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.folder-gallery .folder-empty span[data-v-c53732c8]{display:block;text-align:center;font-size:14px;line-height:16px}.folder-gallery .folder-empty span.ngrm-icon-folder[data-v-c53732c8]{color:#999;font-size:33px}.folder-gallery .folder-empty span strong[data-v-c53732c8]{display:block;margin:5px 0;font-size:16px;line-height:19px}.folder-gallery .load-more-wrapper[data-v-c53732c8]{padding:8px 15px;background-color:#fff;text-align:right;-webkit-box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;position:absolute;bottom:0;left:0;right:0}.ng-spinner[data-v-c53732c8]{position:fixed;vertical-align:center;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.ng-spinner[data-v-c53732c8]:before{display:inline-block;-webkit-animation:spinning 1.5s linear infinite;animation:spinning 1.5s linear infinite}.loading[data-v-2676fd5e]{opacity:.5}.input-file-name-wrapper[data-v-2676fd5e]{padding:8px 15px;background-color:#fff;-webkit-box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;position:absolute;bottom:0;left:0;right:0}.input-file-name-wrapper input[type=text][data-v-2676fd5e]{width:40%;min-width:300px;border:1px solid #e4e4e4;padding:10px 10px;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;margin-right:10px}.input-file-name-wrapper input[type=text].error[data-v-2676fd5e]{border:1px solid red}.input-file-name-wrapper .v-select[data-v-2676fd5e]{width:15%;min-width:150px}.input-file-name-wrapper button[data-v-2676fd5e]{float:right}.input-file-name-wrapper div.error[data-v-2676fd5e]{color:red;margin-bottom:5px}.ng-spinner[data-v-2676fd5e]{position:fixed;vertical-align:middle;margin-top:15%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.ng-spinner[data-v-2676fd5e]:before{display:inline-block;-webkit-animation:spinning 1.5s linear infinite;animation:spinning 1.5s linear infinite} \ No newline at end of file +@import url(https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css);@font-face{font-family:ngri;src:url("data:application/x-font-ttf;charset=utf-8;base64,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AQECAgQCAQQCAQIBAQMBAgQDCRUMDBkODR0ODx4PAwgEAQQCAgMCBAcEAwgDAgMCAgMCBAYEAwcDAgMBAQIBAgEDBgMCAgIBAwIDBQMDBQIBASUaAQEDBgICAgIBBAECBAIFCQUFCwUDBgMCBgQBAwIBAwIDBwMBAgECAQEEAQQHAwQHAwIDAgIDAgQHBAMHBAIDAgIDAgQHBA4cDQ0ZCwwUCgkPBwEDAgEBAQICAgEBAwEBAQEBAgECAQEBAQEBARslAQEBAQEBAQECAQEBAQEBAQEBAQEBAgEBAQEBAQEDAQEDAgEBAQECAgQCAQQCAQIBAQMBAgQDCRUMDBkODR0ODx4PAwgEAQQCAgMCBAcEAwgDAgMCAgMCBAYEAwcDAgMBAQIBAgEDBgMCAgIBAwIDBQMDBQIBAQEmGgEBAwUDAQMCAQMCAgQCBAoFBQoGAwUDAwYDAgMCAQMCAwcDAQEBAQIBAQQBBAYEAwcEAgMCAgMCBAcDBAcEAgMCAgMCBAcDDxsODRkLDBQJCRAHAQMCAgECAQMBAQIBAQEBAQECAQEBAQEBAQEBARokSgIBAQEBAQEBAgEBAQEBAQECAQEDAgIBAgIDAgEEAgEBAQECAQIEAggTCgsWDAwZDQwaDQQGAwIDAgEDAgMGBAMGAwIDAQIDAQMGAwMFAwICAgEBAQIDBQMBAwEBAwECBQMCBAMECAQEBgMBAwECAwECAwEDBQIBARcjAwEBAgUDAgYCAgICAQMBAwYCAQIBAQECAwEDBgMDBgMCAwECAwEDBwMDBgMCAwECAwIDBgMMGAwLFQoKEQgIDQUCAgEBAQECAQIBAQIBAQEBAgEBAQEBAQEBAQEBHhYBAQEBAQEBAQEBAgEBAQEBAQMBAQMBAQEBAQEBAgMBAgQBAQIBAQIBAgQCCBMKCxYMDBkMDRoNAwcDAgMBAgMCAwYDBAYDAQMCAQMCAwYDAwUDAgICAgEBAQMFAwEDAQEDAQIFAgMEAwQIBAMHAwEDAQIDAQIDAQIFAwEBFyIEAgUDAgYCAgICAQMBAwYCAQIBAQECAwEDBgMDBgMCAwECAwEDBwMDBgMCAwECAwIDBgMMGAwLFQoKEQgIDQUCAgEBAQECAQIBAQIBAQEBAgEBAQEBAQEBAQEBHxYBvgMFAwICAgEDAgIEAgUJBQUKBgMGAgMGAwIDAgEDAgMHAwEBAQECAQEEAQQHAwMHBAIDAgIDAgQHAwQHBAIDAgIDAgQHBA4cDQ0ZCwwUCgkPBwEDAgIBAgICAQECAQEBAQEBAgEBAQEBAQEBAQEaJQEBAQEBAQEBAgEBAQEBAQEBAQEBAQIBAQEBAQEBAwEBAwIBAQEBAgIEAgEEAgECAQEDAQIEAwkVDAwZDg0dDg8eDwMIBAEEAgIDAgQHBAMIAwIDAgIDAgQGBAMHAwIDAQECAQIBAwYDAgICAQMCAwUDAwUCAQEmGgEBAwUDAgICAQMCAgQCBQkFBQoGAwYCAwYDAgMCAQMCAwcDAQEBAQIBAQQBBAcDAwcEAgMCAgMCBAcDBAcEAgMCAgMCBAcEDhwNDRkLDBQKCQ8HAQMCAgECAgIBAQIBAQEBAQECAQEBAQEBAQEBARolAQEBAQEBAQECAQEBAQEBAQEBAQEBAgEBAQEBAQEDAQEDAgEBAQECAgQCAQQCAQIBAQMBAgQDCRUMDBkODR0ODx4PAwgEAQQCAgMCBAcEAwgDAgMCAgMCBAYEAwcDAgMBAQIBAgEDBgMCAgIBAwIDBQMDBQIBAQElGgEBQQECBQMCBgICAgIBAwEDBgIBAgEBAQIDAQMGAwMGAwIDAQIDAQMHAwMGAwIDAQIDAgMGAwwYDAsVCgoRCAgNBQICAQEBAQIBAgEBAgEBAQECAQEBAQEBAQEBAQEeFwEBAQEBAQEBAQECAQEBAQECAgEBAwEBAgEBAQIDAgEEAgIBAQIBAgQCCBMKCxYMDBkNDBoNBAYDAgMCAQMCAwYEAwYDAQMCAQMCAwYDAwUDAgICAgECAwUDAQMBAQMBAgUDAgQDBAgEAwcDAQMBAgMBAgMBAgUDAQEXIgQBAgUDAgYCAgICAQMBAwYCAQIBAQECAwEDBgMDBgMCAwECAwEDBwMDBgMCAwECAwIDBgMMGAwLFQoKEQgIDQUCAgEBAQECAQIBAQIBAQEBAgEBAQEBAQEBAQEBHhYBAQEBAQEBAQECAQEBAQECAgEBAwEBAgEBAQIDAgEEAgIBAQIBAgQCCBMKCxYMDBkNDBoNBAYDAgMCAQMCAwYDBAYDAQMCAQMCAwYDAwUDAgICAgECAwUDAQMBAQMBAgUDAgQDBAgEAwcDAQMBAgMBAgMBAgUDAQEXIwMAAAAAAQAC/8ID/gO+AFMAACU4ATEJATgBMT4BNzYmLwEuAQcOAQc4ATEJATgBMS4BJyYGDwEOARceARc4ATEJATgBMQ4BBwYWHwEeATc+ATc4ATEJATgBMR4BFxY2PwE+AScuAQP3/skBNwIEAQMDB5MHEgkDBgL+yf7JAgYDCRIHkwcDAwEEAgE3/skCBAEDAweTBxIJAwYCATcBNwIGAwkSB5MHAwMBBIkBNwE3AgYDCRIHkwcDAwEEAv7JATcCBAEDAweTBxIJAwYC/sn+yQIGAwkSB5MHAwMBBAIBN/7JAgQBAwMHkwcSCQMGAAABAAAAgAQAAsAAKgAAATQmJyYnLgEnJiMiBgcuASMiBhUUFhUuASMiBw4BBwYVFBceARcWMyEyNgQATDkBExNBKyoxOWEhEjcgOE4BCBEJKCQjNQ8PDw81IyQoApBIZgEuPl4OMCorPxISMSoYHE43BQoEAQIQDzQkIygoJCM1Dw9mAAAEAAAAQAQAA0AACwAXACsALwAAATQ2MzIWFRQGIyImJTQ2MzIWFRQGIyImBTU0JiMhIgYVERQWMyEyNj0BBREBITUhAYBeQkJeXkJCXv6AXkJCXl5CQl4DACYa/YAaJiYaAoAaJgEA/oD+AAIAAqBCXl5CQl5eQkJeXkJCXl7+YBomJhr+wBomJhpgoAHA/sDAAAAAAgBA/8ADwAPAABQAJgAAAREhIiY1NDYzIREhIgYVERQWMyERATE4ATEiBhUUFjM4ATkBITUhA4D9YCg4OCgCYP2ANUtLNQMA/SANExMNAmD9oANA/MA4KCg4AwBLNf0ANUsDgP1AEw0NE0AAAQAAAAEAACKXau1fDzz1AAsEAAAAAADWNiSXAAAAANY2JJcAAP/ABAADwAAAAAgAAgAAAAAAAAABAAADwP/AAAAEAAAAAAAEAAABAAAAAAAAAAAAAAAAAAAACgQAAAAAAAAAAAAAAAIAAAAEAAAOBAAAAAQAAAIEAAAABAAAAAQAAEAAAAAAAAoAFAAeAGoGPga0BvYHQAd2AAAAAQAAAAoEhwAEAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAQAAAABAAAAAAACAAcARQABAAAAAAADAAQALQABAAAAAAAEAAQAWgABAAAAAAAFAAsADAABAAAAAAAGAAQAOQABAAAAAAAKABoAZgADAAEECQABAAgABAADAAEECQACAA4ATAADAAEECQADAAgAMQADAAEECQAEAAgAXgADAAEECQAFABYAFwADAAEECQAGAAgAPQADAAEECQAKADQAgG5ncmkAbgBnAHIAaVZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMG5ncmkAbgBnAHIAaW5ncmkAbgBnAHIAaVJlZ3VsYXIAUgBlAGcAdQBsAGEAcm5ncmkAbgBnAHIAaUZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") format("truetype");font-weight:400;font-style:normal}@font-face{font-family:fa-ngrm;src:url(../fonts/fa-ngrm.eot);src:url(../fonts/fa-ngrm.eot#iefix) format("embedded-opentype"),url(data:font/woff2;base64,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) format("woff2"),url(data:font/woff;base64,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) format("woff"),url(../fonts/fa-ngrm.ttf) format("truetype"),url(../img/fa-ngrm.svg#fa-ngrm) format("svg");font-weight:400;font-style:normal}[class*=" ngrm-icon-"]:before,[class^=ngrm-icon-]:before{font-family:fa-ngrm;font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;margin-right:.2em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;margin-left:.2em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.ngrm-icon-cancel:before{content:"\E800"}.ngrm-icon-floppy:before{content:"\E801"}.ngrm-icon-ok:before{content:"\E802"}.ngrm-icon-ccw:before{content:"\E803"}.ngrm-icon-folder:before{content:"\E804"}.ngrm-icon-upload:before{content:"\E805"}.ngrm-icon-play:before{content:"\E811"}.ngrm-icon-trash:before{content:"\F1F8"}.ngrm-icon-file-archive-o:before{content:"\F1C6"}.v-select{position:relative;font-family:inherit}.v-select,.v-select *{-webkit-box-sizing:border-box;box-sizing:border-box}@-webkit-keyframes vSelectSpinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes vSelectSpinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.vs__fade-enter-active,.vs__fade-leave-active{-webkit-transition:opacity .15s cubic-bezier(1,.5,.8,1);transition:opacity .15s cubic-bezier(1,.5,.8,1)}.vs__fade-enter,.vs__fade-leave-to{opacity:0}.vs--disabled .vs__clear,.vs--disabled .vs__dropdown-toggle,.vs--disabled .vs__open-indicator,.vs--disabled .vs__search,.vs--disabled .vs__selected{cursor:not-allowed;background-color:#f8f8f8}.v-select[dir=rtl] .vs__actions{padding:0 3px 0 6px}.v-select[dir=rtl] .vs__clear{margin-left:6px;margin-right:0}.v-select[dir=rtl] .vs__deselect{margin-left:0;margin-right:2px}.v-select[dir=rtl] .vs__dropdown-menu{text-align:right}.vs__dropdown-toggle{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0 0 4px 0;background:none;border:1px solid rgba(60,60,60,.26);border-radius:4px;white-space:normal}.vs__dropdown-toggle,.vs__selected-options{display:-webkit-box;display:-ms-flexbox;display:flex}.vs__selected-options{-ms-flex-preferred-size:100%;flex-basis:100%;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:0 2px;position:relative}.vs__actions{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:4px 6px 0 3px}.vs--searchable .vs__dropdown-toggle{cursor:text}.vs--unsearchable .vs__dropdown-toggle{cursor:pointer}.vs--open .vs__dropdown-toggle{border-bottom-color:transparent;border-bottom-left-radius:0;border-bottom-right-radius:0}.vs__open-indicator{fill:rgba(60,60,60,.5);-webkit-transform:scale(1);transform:scale(1);-webkit-transition:-webkit-transform .15s cubic-bezier(1,-.115,.975,.855);transition:-webkit-transform .15s cubic-bezier(1,-.115,.975,.855);transition:transform .15s cubic-bezier(1,-.115,.975,.855);transition:transform .15s cubic-bezier(1,-.115,.975,.855),-webkit-transform .15s cubic-bezier(1,-.115,.975,.855);-webkit-transition-timing-function:cubic-bezier(1,-.115,.975,.855);transition-timing-function:cubic-bezier(1,-.115,.975,.855)}.vs--open .vs__open-indicator{-webkit-transform:rotate(180deg) scale(1);transform:rotate(180deg) scale(1)}.vs--loading .vs__open-indicator{opacity:0}.vs__clear{fill:rgba(60,60,60,.5);padding:0;border:0;background-color:transparent;cursor:pointer;margin-right:8px}.vs__dropdown-menu{display:block;position:absolute;top:calc(100% - 1px);left:0;z-index:1000;padding:5px 0;margin:0;width:100%;max-height:350px;min-width:160px;overflow-y:auto;-webkit-box-shadow:0 3px 6px 0 rgba(0,0,0,.15);box-shadow:0 3px 6px 0 rgba(0,0,0,.15);border:1px solid rgba(60,60,60,.26);border-top-style:none;border-radius:0 0 4px 4px;text-align:left;list-style:none;background:#fff}.vs__no-options{text-align:center}.vs__dropdown-option{line-height:1.42857143;display:block;padding:3px 20px;clear:both;color:#333;white-space:nowrap}.vs__dropdown-option:hover{cursor:pointer}.vs__dropdown-option--highlight{background:#5897fb;color:#fff}.vs__selected{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;background-color:#f0f0f0;border:1px solid rgba(60,60,60,.26);border-radius:4px;color:#333;line-height:1.4;margin:4px 2px 0 2px;padding:0 .25em}.vs__deselect{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-appearance:none;-moz-appearance:none;appearance:none;margin-left:4px;padding:0;border:0;cursor:pointer;background:none;fill:rgba(60,60,60,.5);text-shadow:0 1px 0 #fff}.vs--single .vs__selected{background-color:transparent;border-color:transparent}.vs--single.vs--open .vs__selected{position:absolute;opacity:.4}.vs--single.vs--searching .vs__selected{display:none}.vs__search::-ms-clear,.vs__search::-webkit-search-cancel-button,.vs__search::-webkit-search-decoration,.vs__search::-webkit-search-results-button,.vs__search::-webkit-search-results-decoration{display:none}.vs__search,.vs__search:focus{-webkit-appearance:none;-moz-appearance:none;appearance:none;line-height:1.4;font-size:1em;border:1px solid transparent;border-left:none;outline:none;margin:4px 0 0 0;padding:0 7px;background:none;-webkit-box-shadow:none;box-shadow:none;width:0;max-width:100%;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.vs__search::-webkit-input-placeholder{color:inherit}.vs__search::-moz-placeholder{color:inherit}.vs__search:-ms-input-placeholder{color:inherit}.vs__search::-ms-input-placeholder{color:inherit}.vs__search::placeholder{color:inherit}.vs--unsearchable .vs__search{opacity:1}.vs--unsearchable .vs__search:hover{cursor:pointer}.vs--single.vs--searching:not(.vs--open):not(.vs--loading) .vs__search{opacity:.2}.vs__spinner{-ms-flex-item-align:center;align-self:center;opacity:0;font-size:5px;text-indent:-9999em;overflow:hidden;border-top:.9em solid hsla(0,0%,39.2%,.1);border-right:.9em solid hsla(0,0%,39.2%,.1);border-bottom:.9em solid hsla(0,0%,39.2%,.1);border-left:.9em solid rgba(60,60,60,.45);-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-animation:vSelectSpinner 1.1s linear infinite;animation:vSelectSpinner 1.1s linear infinite;-webkit-transition:opacity .1s;transition:opacity .1s}.vs__spinner,.vs__spinner:after{border-radius:50%;width:5em;height:5em}.vs--loading .vs__spinner{opacity:1}.ngremotemedia-select-folder-container,.ngrm-model-portal+form,.ngrm-model-portal .ngrm-overlay{-webkit-box-sizing:border-box;box-sizing:border-box;font-family:Roboto,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#333}.ngremotemedia-select-folder-container *,.ngremotemedia-select-folder-container :after,.ngremotemedia-select-folder-container :before,.ngrm-model-portal+form *,.ngrm-model-portal+form :after,.ngrm-model-portal+form :before,.ngrm-model-portal .ngrm-overlay *,.ngrm-model-portal .ngrm-overlay :after,.ngrm-model-portal .ngrm-overlay :before{-webkit-box-sizing:inherit;box-sizing:inherit}.ngremotemedia-select-folder-container .btn,.ngrm-model-portal+form .btn,.ngrm-model-portal .ngrm-overlay .btn{border-radius:4px;text-align:center;font-size:14px;line-height:18px;color:#fff;padding:8px 16px;border:1px solid #e4e4e4;background:#fff;color:#333}.ngremotemedia-select-folder-container .btn:active,.ngrm-model-portal+form .btn:active,.ngrm-model-portal .ngrm-overlay .btn:active{background:#d7d7d7}.ngremotemedia-select-folder-container .btn.btn-blue,.ngrm-model-portal+form .btn.btn-blue,.ngrm-model-portal .ngrm-overlay .btn.btn-blue{background:#009ac7;border:0;color:#fff}.ngremotemedia-select-folder-container .btn.btn-blue:active,.ngrm-model-portal+form .btn.btn-blue:active,.ngrm-model-portal .ngrm-overlay .btn.btn-blue:active{background:rgb(0,114.5326633166,148)}.ngremotemedia-select-folder-container .v-select,.ngrm-model-portal+form .v-select,.ngrm-model-portal .ngrm-overlay .v-select{font-size:14px;line-height:16px;background-color:#fff}.ngremotemedia-select-folder-container .v-select .vs__dropdown-toggle,.ngrm-model-portal+form .v-select .vs__dropdown-toggle,.ngrm-model-portal .ngrm-overlay .v-select .vs__dropdown-toggle{border-radius:0;border:1px solid #e4e4e4;padding:3px 5px 6px}.ngremotemedia-select-folder-container .v-select input::-webkit-input-placeholder,.ngrm-model-portal+form .v-select input::-webkit-input-placeholder,.ngrm-model-portal .ngrm-overlay .v-select input::-webkit-input-placeholder{color:#333;font-style:italic}.ngremotemedia-select-folder-container .v-select input::-moz-placeholder,.ngrm-model-portal+form .v-select input::-moz-placeholder,.ngrm-model-portal .ngrm-overlay .v-select input::-moz-placeholder{color:#333;font-style:italic}.ngremotemedia-select-folder-container .v-select input:-ms-input-placeholder,.ngrm-model-portal+form .v-select input:-ms-input-placeholder,.ngrm-model-portal .ngrm-overlay .v-select input:-ms-input-placeholder{color:#333;font-style:italic}.ngremotemedia-select-folder-container .v-select input::-ms-input-placeholder,.ngrm-model-portal+form .v-select input::-ms-input-placeholder,.ngrm-model-portal .ngrm-overlay .v-select input::-ms-input-placeholder{color:#333;font-style:italic}.ngremotemedia-select-folder-container .v-select input::placeholder,.ngrm-model-portal+form .v-select input::placeholder,.ngrm-model-portal .ngrm-overlay .v-select input::placeholder{color:#333;font-style:italic}.ngremotemedia-select-folder-container ::-webkit-input-placeholder,.ngrm-model-portal+form ::-webkit-input-placeholder,.ngrm-model-portal .ngrm-overlay ::-webkit-input-placeholder{color:#999}.ngremotemedia-select-folder-container :-ms-input-placeholder,.ngrm-model-portal+form :-ms-input-placeholder,.ngrm-model-portal .ngrm-overlay :-ms-input-placeholder{color:#999}.ngremotemedia-select-folder-container ::-moz-placeholder,.ngrm-model-portal+form ::-moz-placeholder,.ngrm-model-portal .ngrm-overlay ::-moz-placeholder{color:#999}.ngremotemedia-select-folder-container ::-ms-input-placeholder,.ngrm-model-portal+form ::-ms-input-placeholder,.ngrm-model-portal .ngrm-overlay ::-ms-input-placeholder{color:#999}.ngremotemedia-select-folder-container ::placeholder,.ngrm-model-portal+form ::placeholder,.ngrm-model-portal .ngrm-overlay ::placeholder{color:#999}.ngrm-model-portal .ngrm-overlay .ng-icon{font-size:4.5em;font-family:ngri;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;text-align:center;line-height:80px;display:block;color:#333}.ngrm-model-portal .ngrm-overlay .ng-icon.big{font-size:64px;color:#333}.ngrm-model-portal .ngrm-overlay .ng-icon.ng-warning:before{content:"\E900"}.ngrm-model-portal .ngrm-overlay .ng-icon.ng-spinner:before{content:"\E901"}.ngrm-model-portal .ngrm-overlay .ng-icon.ng-close:before{content:"\E902"}.ngrm-model-portal .ngrm-overlay .ng-icon.ng-cloud:before{content:"\E903"}.ngrm-model-portal .ngrm-overlay .ng-icon.ng-video:before{content:"\E904"}.ngrm-model-portal .ngrm-overlay .ng-icon.ng-book:before{content:"\E905"}.ngrm-model-portal .ngrm-overlay .image-meta{overflow:visible}.ngrm-model-portal .ngrm-overlay .image-meta h3{font-weight:700}.ngrm-model-portal .ngrm-overlay .image-meta p{color:#737373}.ngrm-model-portal .ngrm-overlay .image-meta input.media-alttext,.ngrm-model-portal .ngrm-overlay .image-meta input.media-caption,.ngrm-model-portal .ngrm-overlay .image-meta input.media-watermarktext{width:100%;border-radius:0;border:1px solid #e4e4e4;padding:7px 10px 7px 10px;margin-bottom:10px}.ngrm-model-portal .ngrm-overlay .image-meta .image-meta-data{margin-top:20px}.ngrm-model-portal .ngrm-overlay .image-wrap:has(.icon-doc){position:relative;height:200px;max-width:400px;display:block;margin-bottom:4px}.ngrm-model-portal .ngrm-overlay .image-wrap:has(.icon-doc):before{position:absolute;content:""}.ngrm-model-portal .ngrm-overlay .image-wrap:has(.icon-doc):before{background-color:rgba(0,0,0,.7);top:0;bottom:0;left:0;right:0}.ngrm-model-portal .ngrm-overlay .image-wrap .icon-doc{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#fff;font-size:70px}.ngrm-model-portal .ngrm-overlay .image-wrap img{max-width:100%}.ngrm-model-portal .ngrm-overlay .vue-treeselect{font-size:14px;line-height:16px}.ngrm-model-portal .ngrm-overlay .vue-treeselect .vue-treeselect__control{border-radius:0;border:1px solid #e4e4e4}.ngrm-model-portal .ngrm-overlay .vue-treeselect .vue-treeselect__placeholder{color:#333;font-style:italic}@-webkit-keyframes spinning{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes spinning{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.ngremotemedia-buttons{margin-top:20px}.ngremotemedia-buttons .ngremotemedia-local-file-container{margin:10px 0 10px 0}.ngremotemedia-buttons .ngremotemedia-local-file.btn.upload-from-disk,.ngremotemedia-buttons input[type=button]{border-radius:4px;text-align:center;font-size:14px!important;line-height:18px;padding:8px 16px;border:1px solid #e4e4e4;background:#f1f4fa;cursor:pointer;color:#333;font-weight:400;font-family:Arial,Helvetica,sans-serif}.ngremotemedia-buttons .ngremotemedia-local-file.btn.upload-from-disk{background:#fff}.o2k7Skin .mceIcon.mce_ngremotemedia img{width:2rem}.ngremotemedia-image .image-wrap .file-placeholder[data-v-77f74934]{position:relative;max-width:500px;height:280px;display:block;margin-bottom:4px}.ngremotemedia-image .image-wrap .file-placeholder .icon-doc[data-v-77f74934]{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#fff;font-size:40px}.ngremotemedia-image .image-wrap .file-placeholder[data-v-77f74934]:before{position:absolute;content:"";background-color:rgba(0,0,0,.7);top:0;bottom:0;left:0;right:0}.mediaFacets[data-v-278bb214]{width:362px;-ms-flex-negative:0;flex-shrink:0;-webkit-box-shadow:inset -1px 0 0 0 #e4e4e4;box-shadow:inset -1px 0 0 0 #e4e4e4}.mediaFacets .body[data-v-278bb214]{-webkit-box-shadow:inset 0 -1px 0 0 #e4e4e4,inset 0 1px 0 0 #e4e4e4,inset -1px 0 0 0 #e4e4e4;box-shadow:inset 0 -1px 0 0 #e4e4e4,inset 0 1px 0 0 #e4e4e4,inset -1px 0 0 0 #e4e4e4;background:#fff;padding:30px 15px}.mediaFacets .body .form-field+.form-field[data-v-278bb214]{margin-top:15px}.mediaFacets .body .form-field label[data-v-278bb214],.mediaFacets .body .search-wrapper .search-label[data-v-278bb214]{font-size:12px;font-weight:700;line-height:18px;color:#757575;margin-bottom:3px;display:block}.mediaFacets .body .search-wrapper[data-v-278bb214]{margin:30px 0 0}.mediaFacets .body .search-wrapper .search[data-v-278bb214]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:5px 0}.mediaFacets .body .search-wrapper .search input[data-v-278bb214],.mediaFacets .body .search-wrapper .search ul[data-v-278bb214]{font-size:14px;line-height:16px}.mediaFacets .body .search-wrapper .search ul[data-v-278bb214]{margin:0;padding:5px;list-style:none;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:1px solid #e4e4e4;min-width:75px;display:none}.mediaFacets .body .search-wrapper .search ul li[data-v-278bb214]{cursor:auto;margin-right:10px;padding:4px 10px;min-width:45px}.mediaFacets .body .search-wrapper .search ul li[data-v-278bb214]:last-child,.mediaFacets .body .search-wrapper .search ul li[data-v-278bb214]:only-child{margin:0}.mediaFacets .body .search-wrapper .search ul li.active[data-v-278bb214]{background:#009ac7;color:#fff;border-radius:4px;-webkit-box-shadow:inset -1px 0 0 0 #d7d7d7,inset 1px 0 0 0 #d7d7d7,inset 0 1px 0 0 #d7d7d7,inset 0 -1px 0 0 #d7d7d7;box-shadow:inset -1px 0 0 0 #d7d7d7,inset 1px 0 0 0 #d7d7d7,inset 0 1px 0 0 #d7d7d7,inset 0 -1px 0 0 #d7d7d7}.mediaFacets .body .search-wrapper .search input[data-v-278bb214]{border:1px solid #e4e4e4;padding:9px 10px;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.mediaFacets .ng-spinner[data-v-278bb214]{position:fixed;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.mediaFacets .ng-spinner[data-v-278bb214]:before{display:inline-block;-webkit-animation:spinning 1.5s linear infinite;animation:spinning 1.5s linear infinite}.media-gallery[data-v-5aa52c90]{position:relative;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.media-gallery .items[data-v-5aa52c90]{padding:15px;overflow-y:auto;height:calc(100% - 50px)}.media-gallery .items.loading[data-v-5aa52c90]{opacity:.5}.media-gallery .items .media[data-v-5aa52c90]{width:190px;min-height:182px;max-height:190px;padding:8px;margin:0 15px 15px 0;background-color:#fff;display:inline-block}.media-gallery .items .media .media-container[data-v-5aa52c90]{width:100%}.media-gallery .items .media .img[data-v-5aa52c90]{display:block;margin-bottom:4px;-o-object-fit:cover;object-fit:cover;height:100px;width:100%;overflow:hidden;text-overflow:ellipsis}.media-gallery .items .media .file-placeholder[data-v-5aa52c90]{position:relative;height:95px;display:block;margin-bottom:4px}.media-gallery .items .media .file-placeholder .icon-doc[data-v-5aa52c90]{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#fff;font-size:40px}.media-gallery .items .media .file-placeholder[data-v-5aa52c90]:before{position:absolute;content:"";background-color:rgba(0,0,0,.7);top:0;bottom:0;left:0;right:0}.media-gallery .items .media .filename[data-v-5aa52c90]{overflow:hidden;display:inline-block;text-overflow:ellipsis;white-space:nowrap;width:100%;text-align:center;font-size:16px;line-height:20px;margin-top:4px;margin-bottom:0}.media-gallery .items .media .size-description[data-v-5aa52c90]{font-size:12px;line-height:14px;text-align:center;color:#999}.media-gallery .items .media .size-description .format[data-v-5aa52c90]{text-transform:uppercase}.media-gallery .items .media.selected[data-v-5aa52c90]{border:1px solid #009ac7}.media-gallery .items .media .select-btn[data-v-5aa52c90]{margin-top:8px;padding:3px;width:100%}.media-gallery .folder-empty[data-v-5aa52c90]{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.media-gallery .folder-empty span[data-v-5aa52c90]{display:block;text-align:center;font-size:14px;line-height:16px}.media-gallery .folder-empty span.ngrm-icon-folder[data-v-5aa52c90]{color:#999;font-size:33px}.media-gallery .folder-empty span strong[data-v-5aa52c90]{display:block;margin:5px 0;font-size:16px;line-height:19px}.media-gallery .load-more-wrapper[data-v-5aa52c90]{padding:8px 15px;background-color:#fff;text-align:right;-webkit-box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;position:absolute;bottom:0;left:0;right:0}.ngrm-overlay[data-v-9d33d07a]{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(0,0,0,.8);z-index:9999}.ngrm-overlay .media-modal[data-v-9d33d07a]{background-color:#f5f5f5;-webkit-box-shadow:0 5px 15px 0 rgba(0,0,0,.5);box-shadow:0 5px 15px 0 rgba(0,0,0,.5);margin:32px;height:calc(100vh - 64px)}.ngrm-overlay .media-modal .title[data-v-9d33d07a]{padding:15px;font-size:16px;font-weight:700;line-height:20px;color:#333;background:#fff;text-transform:uppercase;-webkit-box-shadow:inset 0 -1px 0 0 #e4e4e4;box-shadow:inset 0 -1px 0 0 #e4e4e4}.ngrm-overlay .media-modal .title .close[data-v-9d33d07a]{float:right;cursor:pointer;padding:2px 10px}.ngrm-overlay .media-modal .body[data-v-9d33d07a]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;position:relative;height:calc(100% - 50px)}.ngrm-overlay .img-placeholder img[data-v-9d33d07a]{height:100%}.ngremotemedia-buttons input[type=button]{margin-bottom:4px;margin-right:4px}.help-block.description{margin-right:6px}.btn,.sidebar-crop .buttons{font-size:14px!important;font-family:Arial,Helvetica,sans-serif}.ngremotemedia-tags .vs__selected-options input[type=search].vs__search{border:none}.ngremotemedia-tags select[hidden=hidden]{display:none}.ngremotemedia-image h3.title{word-break:break-word}.ngremotemedia-image .image-wrap img{max-width:100%}.input-file-name-wrapper{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.input-file-name-wrapper .v-select,.input-file-name-wrapper input[type=text]{margin-right:16px}.input-file-name-wrapper .v-select.vs--single.vs--searchable{margin-right:10px}.input-file-name-wrapper .v-select.vs--single.vs--searchable input.vs__search{background:transparent}.input-file-name-wrapper button.btn{margin-left:40%;font-size:14px!important;font-family:Arial}.input-file-name-wrapper .vs__dropdown-toggle{height:37px}.media-gallery .items .media{line-height:normal}.ng-spinner[data-v-751395c8]{position:fixed;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.ng-spinner[data-v-751395c8]:before{display:inline-block;-webkit-animation:spinning 1.5s linear infinite;animation:spinning 1.5s linear infinite}.sidebar-crop[data-v-ad171770]{width:264px;-ms-flex-negative:0;flex-shrink:0;-webkit-box-shadow:inset -1px 0 0 0 #e4e4e4;box-shadow:inset -1px 0 0 0 #e4e4e4}.sidebar-crop .buttons[data-v-ad171770]{background:#fff;padding:15px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-shadow:inset 0 -1px 0 0 #e4e4e4;box-shadow:inset 0 -1px 0 0 #e4e4e4;margin-right:1px}.sidebar-crop .buttons button[data-v-ad171770]{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.sidebar-crop .buttons button.crop-btn-add[data-v-ad171770]{margin-left:10px}.sidebar-crop .buttons button[data-v-ad171770]:only-child{width:100%}.sidebar-crop-label span[data-v-ad171770]{color:#999;font-size:14px;line-height:18px;display:inline-block;padding:31px 15px 15px;width:100%;-webkit-box-shadow:inset 0 -1px 0 0 #e4e4e4;box-shadow:inset 0 -1px 0 0 #e4e4e4}.unselectedVariations[data-v-ad171770]{position:absolute;top:0;left:0;width:264px;height:100%;-webkit-transform:translateX(264px);transform:translateX(264px);background:#fff;-webkit-box-shadow:inset -1px 0 0 0 #e4e4e4;box-shadow:inset -1px 0 0 0 #e4e4e4;z-index:10}.unselectedVariations>div[data-v-ad171770]{padding:15px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;background-color:#fff;-webkit-box-shadow:inset 0 -1px 0 0 #e4e4e4,inset -1px 0 0 0 #e4e4e4;box-shadow:inset 0 -1px 0 0 #e4e4e4,inset -1px 0 0 0 #e4e4e4}.unselectedVariations>div input[data-v-ad171770],.unselectedVariations>div label[data-v-ad171770]{cursor:pointer}.unselectedVariations>div input[data-v-ad171770]{margin-right:11px}.unselectedVariations>div label[data-v-ad171770]{width:100%}.unselectedVariations>div .name[data-v-ad171770]{color:#333;font-size:14px;line-height:18px}.unselectedVariations>div .formatted-size[data-v-ad171770]{color:#999;font-size:12px;line-height:18px;display:block}.unselectedVariations>div.disabled[data-v-ad171770]{-ms-flex-wrap:wrap;flex-wrap:wrap;background-color:#f5f5f5;cursor:auto;color:#ddd;padding:15px 15px 5px}.unselectedVariations>div.disabled input[data-v-ad171770],.unselectedVariations>div.disabled label[data-v-ad171770],.unselectedVariations>div.disabled span[data-v-ad171770]{cursor:auto}.unselectedVariations>div.disabled label[data-v-ad171770]{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;width:auto;color:#999}.unselectedVariations .legend-not-selectable[data-v-ad171770]{width:100%;font-size:.75rem;color:#a41034;display:inline-block;text-align:right}.selectedVariations ul[data-v-ad171770]{list-style:none;padding:0;margin:0}.selectedVariations ul li[data-v-ad171770]{padding:15px 0 15px 15px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-shadow:inset -1px 0 0 0 #e4e4e4,inset 0 -1px 0 0 #e4e4e4;box-shadow:inset -1px 0 0 0 #e4e4e4,inset 0 -1px 0 0 #e4e4e4;background-color:#fff;cursor:pointer}.selectedVariations ul li.disabled[data-v-ad171770]{background-color:#f5f5f5;cursor:auto}.selectedVariations ul li.selected.set[data-v-ad171770]{color:#90ee90}.selectedVariations ul li span[data-v-ad171770]{display:block;color:#333;font-size:14px;line-height:18px}.selectedVariations ul li a[data-v-ad171770]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.selectedVariations ul li a span[data-v-ad171770]{padding:5px}.selectedVariations ul li a .ngrm-icon-trash[data-v-ad171770]{color:#009ac7;padding:10px}.selectedVariations ul li .formatted-size[data-v-ad171770]{color:#999;font-size:12px;line-height:18px}.selectedVariations ul li .circle-orange[data-v-ad171770]{width:8px;height:8px;background-color:orange;border-radius:50%}.selectedVariations .set .circle-orange[data-v-ad171770]{display:none}.crop .cropper[data-v-5f73791e]{position:relative;margin:0 auto}.crop .cropper button[data-v-5f73791e]{margin-left:8px}.crop .cropper .buttons[data-v-5f73791e]{position:absolute}.crop .preview[data-v-5f73791e]{width:100%;height:500px;overflow:hidden}.crop-container[data-v-45497070]{overflow-y:auto;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;margin:30px 30px 80px}.crop-container[data-v-45497070]:empty{display:none}.img-placeholder[data-v-45497070]{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;padding:60px 60px 110px}.img-placeholder img[data-v-45497070]{max-width:100%;height:auto;margin:0 auto;display:block}.action-strip[data-v-45497070]{padding:8px 15px;background-color:#fff;text-align:right;-webkit-box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;position:absolute;bottom:0;left:264px;right:0}.action-strip button[data-v-45497070]{margin-left:10px}.action-strip .ngrm-icon-floppy[data-v-45497070]{margin-right:5px}.folder-gallery[data-v-c53732c8]{position:relative;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;height:calc(100% - 50px);overflow-y:auto}.folder-gallery .items[data-v-c53732c8]{padding:15px}.folder-gallery .items.loading[data-v-c53732c8]{opacity:.5}.folder-gallery .items .breadcrumbs[data-v-c53732c8]{background-color:#fff;width:100%;margin-bottom:20px;padding:10px}.folder-gallery .items .breadcrumbs a[data-v-c53732c8]{color:#009ac7}.folder-gallery .items .info[data-v-c53732c8]{font-style:italic;margin-bottom:10px;margin-left:10px}.folder-gallery .items .media[data-v-c53732c8]{width:177px;min-height:182px;max-height:190px;padding:8px;margin:0 15px 15px 0;background-color:#fff;display:inline-block}.folder-gallery .items .media .media-container[data-v-c53732c8]{width:100%}.folder-gallery .items .media .img[data-v-c53732c8]{display:block;margin-bottom:4px;-o-object-fit:cover;object-fit:cover;height:92px;width:100%;overflow:hidden;text-overflow:ellipsis}.folder-gallery .items .media .file-placeholder[data-v-c53732c8]{position:relative;height:92px;display:block;margin-bottom:4px}.folder-gallery .items .media .file-placeholder .icon-doc[data-v-c53732c8]{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#fff;font-size:40px}.folder-gallery .items .media .file-placeholder[data-v-c53732c8]:before{position:absolute;content:"";background-color:rgba(0,0,0,.7);top:0;bottom:0;left:0;right:0}.folder-gallery .items .media.new-folder input[data-v-c53732c8]{width:100%;margin-top:5px}.folder-gallery .items .media.new-folder .select-btn[data-v-c53732c8]{background:#2e8b57}.folder-gallery .items .media.new-folder .file-placeholder[data-v-c53732c8]:before{background-color:rgba(0,0,0,.2);top:0;bottom:0;left:0;right:0}.folder-gallery .items .media .filename[data-v-c53732c8]{overflow:hidden;display:inline-block;text-overflow:ellipsis;white-space:nowrap;width:100%;text-align:center;font-size:16px;line-height:20px;margin-top:4px;margin-bottom:0}.folder-gallery .items .media .size-description[data-v-c53732c8]{font-size:12px;line-height:14px;text-align:center;color:#999}.folder-gallery .items .media .size-description .format[data-v-c53732c8]{text-transform:uppercase}.folder-gallery .items .media.selected[data-v-c53732c8]{border:1px solid #009ac7}.folder-gallery .items .media .select-btn[data-v-c53732c8]{margin-top:10px;padding:3px;width:100%}.folder-gallery .folder-empty[data-v-c53732c8]{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.folder-gallery .folder-empty span[data-v-c53732c8]{display:block;text-align:center;font-size:14px;line-height:16px}.folder-gallery .folder-empty span.ngrm-icon-folder[data-v-c53732c8]{color:#999;font-size:33px}.folder-gallery .folder-empty span strong[data-v-c53732c8]{display:block;margin:5px 0;font-size:16px;line-height:19px}.folder-gallery .load-more-wrapper[data-v-c53732c8]{padding:8px 15px;background-color:#fff;text-align:right;-webkit-box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;position:absolute;bottom:0;left:0;right:0}.ng-spinner[data-v-c53732c8]{position:fixed;vertical-align:center;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.ng-spinner[data-v-c53732c8]:before{display:inline-block;-webkit-animation:spinning 1.5s linear infinite;animation:spinning 1.5s linear infinite}.loading[data-v-2676fd5e]{opacity:.5}.input-file-name-wrapper[data-v-2676fd5e]{padding:8px 15px;background-color:#fff;-webkit-box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;box-shadow:inset 1px 0 0 0 #e4e4e4,0 -1px 0 0 #e4e4e4;position:absolute;bottom:0;left:0;right:0}.input-file-name-wrapper input[type=text][data-v-2676fd5e]{width:40%;min-width:300px;border:1px solid #e4e4e4;padding:10px 10px;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;margin-right:10px}.input-file-name-wrapper input[type=text].error[data-v-2676fd5e]{border:1px solid red}.input-file-name-wrapper .v-select[data-v-2676fd5e]{width:15%;min-width:150px}.input-file-name-wrapper button[data-v-2676fd5e]{float:right}.input-file-name-wrapper div.error[data-v-2676fd5e]{color:red;margin-bottom:5px}.ng-spinner[data-v-2676fd5e]{position:fixed;vertical-align:middle;margin-top:15%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.ng-spinner[data-v-2676fd5e]:before{display:inline-block;-webkit-animation:spinning 1.5s linear infinite;animation:spinning 1.5s linear infinite} \ No newline at end of file diff --git a/bundle/Resources/public/js/remotemedia.js b/bundle/Resources/public/js/remotemedia.js index ea7606d9..6830b464 100644 --- a/bundle/Resources/public/js/remotemedia.js +++ b/bundle/Resources/public/js/remotemedia.js @@ -1,2 +1,2 @@ -(function(e){function t(t){for(var i,o,r=t[0],l=t[1],c=t[2],f=0,u=[];f0?e.config.allowedTags:e.allTags,multiple:"",taggable:0===e.config.allowedTags.length},on:{input:e.handleTagsInput},model:{value:e.selectedImage.tags,callback:function(t){e.$set(e.selectedImage,"tags",t)},expression:"selectedImage.tags"}}),a("select",{directives:[{name:"model",rawName:"v-model",value:e.selectedImage.tags,expression:"selectedImage.tags"}],staticClass:"ngremotemedia-newtags",attrs:{hidden:"",name:this.config.inputFields.tags,multiple:"multiple"},on:{change:function(t){var a=Array.prototype.filter.call(t.target.options,(function(e){return e.selected})).map((function(e){var t="_value"in e?e._value:e.value;return t}));e.$set(e.selectedImage,"tags",t.target.multiple?a:a[0])}}},e._l(e.allTags,(function(t){return a("option",{key:t},[e._v(e._s(t))])})),0)],1),a("div",{staticClass:"ngremotemedia-watermark-text"},[a("span",{staticClass:"help-block description"},[e._v("\n "+e._s(this.config.translations.preview_watermark_text)+"\n "),a("i",{staticClass:"fa fa-info-circle",attrs:{"data-toggle":"tooltip","data-placement":"right",title:this.config.translations.preview_watermark_text_info}})]),a("input",{directives:[{name:"model",rawName:"v-model",value:e.selectedImage.watermarkText,expression:"selectedImage.watermarkText"},{name:"debounce",rawName:"v-debounce:500ms",value:e.dispatchChangeEvent,expression:"dispatchChangeEvent",arg:"500ms"}],staticClass:"media-watermarktext data",attrs:{type:"text",name:this.config.inputFields.watermarkText},domProps:{value:e.selectedImage.watermarkText},on:{input:function(t){t.target.composing||e.$set(e.selectedImage,"watermarkText",t.target.value)}}})])])])]):a("div",[a("i",[e._v(e._s(this.config.translations.interactions_no_media_selected))])])}),p=[],h=(a("5df3"),a("4f7f"),a("75fc")),m=(a("28a5"),a("01c8")),g=function(e){var t=[];for(var a in e)t.push(encodeURIComponent(a)+"="+encodeURIComponent(e[a]));return t.join("&")},v=function(e){return e[0].toUpperCase()+e.slice(1)},b=function(e){var t=e.split("-"),a=Object(m["a"])(t),i=a[0],n=a.slice(1);return[i].concat(Object(h["a"])(n.map(v))).join("")},_=function(e,t){var a=Math.pow(10,t);return parseFloat(Math.round(e*a)/a).toFixed(t)},w={B:"KB",KB:"MB",MB:"GB",GB:"TB"},y=function e(t){var a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"B",i=w[a];return!i||t<1024?"".concat(_(t,2)," ").concat(a):e(t/1024,i)},C=a("4a7a"),x=a.n(C),O={name:"Preview",props:["config","fieldId","selectedImage"],data:function(){return{allTags:[]}},components:{"v-select":x.a},computed:{nonImagePreviewClass:function(){return"video"===this.selectedImage.type?"ng-video":"ng-book"},formattedSize:function(){return y(this.selectedImage.size)}},methods:{handleTagsInput:function(e){this.allTags=Object(h["a"])(new Set([].concat(Object(h["a"])(this.allTags),Object(h["a"])(e)))),this.dispatchChangeEvent()},dispatchChangeEvent:function(){this.$emit("preview-change")}},mounted:function(){this.allTags=Object(h["a"])(this.selectedImage.tags)},watch:{selectedImage:function(){this.allTags=Object(h["a"])(this.selectedImage.tags)}}},I=O,F=(a("fc96"),a("2877")),V=Object(F["a"])(I,u,p,!1,null,"2db10e6e",null),k=V.exports,j=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("modal",{attrs:{title:this.config.translations.browse_title},on:{close:function(t){return e.$emit("close")}}},[a("media-facets",{attrs:{config:e.config,tags:e.tags,types:e.types,visibilities:e.visibilities,facets:e.facets,"facets-loading":e.facetsLoading},on:{change:e.handleFacetsChange}}),a("media-gallery",{attrs:{translations:e.config.translations,media:e.media,canLoadMore:e.canLoadMore,selectedMediaId:e.selectedMediaId,loading:e.loading},on:{loadMore:e.handleLoadMore,"media-selected":function(t){return e.$emit("media-selected",t)}}}),e.loading?a("i",{staticClass:"ng-icon ng-spinner"}):e._e()],1)},S=[],M=a("768b"),T=(a("ffc1"),function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"mediaFacets"},[a("div",{staticClass:"body"},[e.types.length>1?a("div",{staticClass:"form-field"},[a("label",{attrs:{for:"type"}},[e._v(e._s(this.config.translations.browse_select_type))]),a("v-select",{attrs:{options:e.types,label:"name",reduce:function(e){return e.id},placeholder:e.facetsLoading?this.config.translations.browse_loading_types:this.config.translations.browse_all_types},on:{input:e.handleTypeChange},model:{value:e.selectedType,callback:function(t){e.selectedType=t},expression:"selectedType"}})],1):e._e(),e.folders&&!this.config.folder?a("div",{staticClass:"form-field"},[a("label",{attrs:{for:"folder"}},[e._v(e._s(this.config.translations.browse_select_folder))]),a("treeselect",{attrs:{multiple:!1,options:e.folders,"load-options":e.loadSubFolders,value:this.config.parentFolder?this.config.parentFolder.id:"",placeholder:e.facetsLoading?this.config.translations.browse_loading_folders:this.config.translations.browse_all_folders,disabled:e.facetsLoading,clearable:!0,beforeClearAll:e.clearFolderField,defaultExpandLevel:1},on:{input:e.handleFolderChange},model:{value:e.selectedFolder,callback:function(t){e.selectedFolder=t},expression:"selectedFolder"}})],1):e._e(),e.tags.length>1?a("div",{staticClass:"form-field"},[a("label",{attrs:{for:"tag"}},[e._v(e._s(this.config.translations.browse_select_tag))]),a("v-select",{attrs:{options:e.tags,label:"name",reduce:function(e){return e.id},placeholder:e.facetsLoading?this.config.translations.browse_loading_tags:this.config.translations.browse_all_tags,disabled:e.facetsLoading},on:{input:e.handleTagChange},model:{value:e.tag,callback:function(t){e.tag=t},expression:"tag"}})],1):e._e(),e.visibilities.length>1?a("div",{staticClass:"form-field"},[a("label",{attrs:{for:"visibilities"}},[e._v(e._s(this.config.translations.browse_select_visibility))]),a("v-select",{attrs:{options:e.visibilities,label:"name",reduce:function(e){return e.id},placeholder:e.facetsLoading?this.config.translations.browse_loading_visibilities:this.config.translations.browse_all_visibilities,disabled:e.facetsLoading},on:{input:e.handleVisibilityChange},model:{value:e.visibility,callback:function(t){e.visibility=t},expression:"visibility"}})],1):e._e(),a("div",{staticClass:"search-wrapper"},[a("span",{staticClass:"search-label"},[e._v(e._s(this.config.translations.search))]),a("div",{staticClass:"search"},[a("input",{directives:[{name:"model",rawName:"v-model",value:e.query,expression:"query"}],attrs:{type:"text",placeholder:this.config.translations.search_placeholder},domProps:{value:e.query},on:{keyup:e.handleQueryChange,keydown:function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")?null:(t.preventDefault(),null(t))},input:function(t){t.target.composing||(e.query=t.target.value)}}})])])])])}),$=[],E="all",P="image",z="video",R="raw",L="(all)",A="(root)",D="all",U=a("ca17"),N=a.n(U),B=(a("542c"),{name:"MediaFacets",props:["config","tags","types","visibilities","facets","facetsLoading"],data:function(){return{TYPE_ALL:E,TYPE_IMAGE:P,TYPE_VIDEO:z,TYPE_RAW:R,FOLDER_ALL:L,FOLDER_ROOT:A,TAG_ALL:D,folders:[{id:this.config.parentFolder?this.config.parentFolder.id:A,label:this.config.parentFolder?this.config.parentFolder.label:A,children:null}],selectedFolder:this.facets.folder,selectedType:this.facets.type,query:this.facets.query,tag:this.facets.tag,visibility:this.facets.visibility}},methods:{clearFolderField:function(){return!this.config.parentFolder||(this.selectedFolder=this.config.parentFolder.id,!1)},handleTypeChange:function(e){this.$emit("change",{type:e})},handleFolderChange:function(e){this.selectedFolder=e,"undefined"!==typeof e&&e||(this.selectedFolder=this.config.parentFolder?this.config.parentFolder.id:e),this.$emit("change",{folder:this.selectedFolder})},handleQueryChange:function(){this.$emit("change",{query:this.query})},handleTagChange:function(){this.$emit("change",{tag:this.tag})},handleVisibilityChange:function(){this.$emit("change",{visibility:this.visibility})},loadSubFolders:function(){var e=Object(d["a"])(regeneratorRuntime.mark((function e(t){var a,i,n;return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:return a=t.parentNode,i={folder:"(root)"===a.id?"":a.id},e.next=4,fetch(this.config.paths.load_folders+"?"+g(i));case 4:return n=e.sent,e.next=7,n.json();case 7:a.children=e.sent,t.callback();case 9:case"end":return e.stop()}}),e,this)})));function t(t){return e.apply(this,arguments)}return t}()},components:{"v-select":x.a,treeselect:N.a}}),q=B,G=(a("c345"),Object(F["a"])(q,T,$,!1,null,"278bb214",null)),W=G.exports,Y=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"media-gallery"},[a("div",{class:e.loading?"items loading":"items"},[e.media.length?e._e():a("div",{staticClass:"folder-empty"},[a("span",{staticClass:"ngrm-icon-folder"}),a("span",[a("strong",[e._v(e._s(this.translations.media_gallery_empty_folder))]),e._v(e._s(this.translations.media_gallery_upload_media))])]),e._l(e.media,(function(t){return a("div",{key:t.id,staticClass:"media",class:{selected:t.remoteId===e.selectedMediaId}},["image"===t.type||"video"===t.type&&""!==t.browseUrl?a("div",{staticClass:"media-container"},[a("img",{staticClass:"img",attrs:{src:t.browseUrl,alt:t.filename}}),a("Label",{staticClass:"filename"},[e._v(e._s(t.filename))]),a("div",{staticClass:"size-description"},["public"===t.visibility?a("i",{staticClass:"fa fa-solid fa-globe"},[e._v("  ")]):e._e(),"private"===t.visibility?a("i",{staticClass:"fa fa-eye-slash"},[e._v("  ")]):e._e(),"protected"===t.visibility?a("i",{staticClass:"fa fa-lock"},[e._v("  ")]):e._e(),a("span",{staticClass:"format"},[e._v(e._s(t.format))]),e._v(" - "+e._s(t.width)+" x "+e._s(t.height)+" - "+e._s(e.showFilesize(t))+"\n ")])],1):a("div",{staticClass:"media-container"},[a("span",{staticClass:"file-placeholder"},[a("span",{staticClass:"icon-doc"},["pdf"===t.format?a("i",{staticClass:"fa fa-file-pdf-o"}):"zip"===t.format||"rar"===t.format?a("i",{staticClass:"fa fa-file-archive-o"}):"ppt"===t.format||"pptx"===t.format?a("i",{staticClass:"fa fa-file-powerpoint-o"}):"doc"===t.format||"docx"===t.format?a("i",{staticClass:"fa fa-file-word-o"}):"xls"===t.format||"xlsx"===t.format?a("i",{staticClass:"fa fa-file-excel-o"}):"aac"===t.format||"aiff"===t.format||"amr"===t.format||"flac"===t.format||"m4a"===t.format||"mp3"===t.format||"ogg"===t.format||"opus"===t.format||"wav"===t.format?a("i",{staticClass:"fa fa-file-audio-o"}):"txt"===t.format?a("i",{staticClass:"fa fa-lg fa-file-text"}):a("i",{staticClass:"fa fa-file"})])]),a("Label",{staticClass:"filename"},[e._v(e._s(t.filename))]),a("div",{staticClass:"size-description"},["public"===t.visibility?a("i",{staticClass:"fa fa-solid fa-globe"},[e._v("  ")]):e._e(),"private"===t.visibility?a("i",{staticClass:"fa fa-eye-slash"},[e._v("  ")]):e._e(),"protected"===t.visibility?a("i",{staticClass:"fa fa-lock"},[e._v("  ")]):e._e(),a("span",{staticClass:"format"},[e._v(e._s(t.format))]),e._v(" - "+e._s(e.showFilesize(t))+"\n ")])],1),a("button",{staticClass:"btn btn-blue select-btn",attrs:{type:"button"},on:{click:function(a){return e.$emit("media-selected",t)}}},[e._v(e._s(e._self.translations.media_gallery_select))])])}))],2),e.canLoadMore?a("div",{staticClass:"load-more-wrapper"},[a("button",{staticClass:"btn btn-blue",attrs:{type:"button"},on:{click:function(t){return e.$emit("loadMore")}}},[e._v(e._s(this.translations.media_gallery_load_more))])]):e._e()])},J=[],K=a("94df"),Q=a.n(K),H={name:"MediaGallery",props:["translations","media","canLoadMore","onLoadMore","selectedMediaId","loading"],methods:{showFilesize:function(e){return Q()(e.size)}}},X=H,Z=(a("bdf2"),Object(F["a"])(X,Y,J,!1,null,"5aa52c90",null)),ee=Z.exports,te=a("b012"),ae=a.n(te),ie=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"ngrm-overlay"},[a("div",{staticClass:"media-modal"},[a("div",{staticClass:"title"},[e._v("\n "+e._s(e.title)+"\n "),a("span",{staticClass:"close",on:{click:e.close}},[a("span",{staticClass:"ngrm-icon-cancel"})])]),a("div",{staticClass:"body"},[e._t("default")],2)])])},ne=[],se={name:"Modal",props:["title"],methods:{close:function(){this.$emit("close")}}},oe=se,re=(a("11ef"),a("a301"),Object(F["a"])(oe,ie,ne,!1,null,"9d33d07a",null)),le=re.exports;function ce(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function de(e){for(var t=1;t0&&void 0!==p[0]?p[0]:{patch:!1},a=t.patch,this.loading=!0,this.abortController&&this.abortController.abort(),this.abortController=new AbortController,i={limit:fe,offset:a?this.media.length:0},this.facets.query&&(i["query"]=this.facets.query),this.config.allowedTypes.length>0&&(i["type"]=this.config.allowedTypes),this.facets.type&&(i["type"]=this.facets.type),this.facets.folder&&(i["folder"]=this.facets.folder===A?"":this.facets.folder),this.config.allowedTags.length>0&&(i["tag"]=this.config.allowedTags),this.facets.tag&&(i["tag"]=this.facets.tag),this.config.allowedVisibilities.length>0&&(i["visibility"]=this.config.allowedVisibilities),this.facets.visibility&&(i["visibility"]=this.facets.visibility),a&&this.nextCursor&&(i["next_cursor"]=this.nextCursor),n="",s=0,o=Object.entries(i);s-1:e.newSelection},on:{change:function(a){var i=e.newSelection,n=a.target,s=!!n.checked;if(Array.isArray(i)){var o=t,r=e._i(i,o);n.checked?r<0&&(e.newSelection=i.concat([o])):r>-1&&(e.newSelection=i.slice(0,r).concat(i.slice(r+1)))}else e.newSelection=s}}}),a("label",{attrs:{for:t}},[a("span",{staticClass:"name"},[e._v(e._s(t))]),a("span",{staticClass:"formatted-size"},[e._v(e._s(e.formattedSize(t)))])]),e.isVariationSelectable(t)?e._e():a("div",{staticClass:"legend-not-selectable"},[a("span",[e._v(e._s(e._self.translations.crop_media_too_small))])])])})),0),a("div",{staticClass:"selectedVariations"},[a("ul",e._l(e.selectedVariations,(function(t){return a("li",{key:t,class:{set:!!e.allVariationValues[t],selected:e.selectedVariation===t,disabled:!e.isVariationSelectable(t)},on:{click:function(a){return e.handleVariationClicked(t)}}},[a("div",[a("span",{staticClass:"name"},[e._v(e._s(t))]),a("span",{staticClass:"formatted-size"},[e._v(e._s(e.formattedSize(t)))])]),e.addingVariations?e._e():a("a",[a("span",{staticClass:"circle-orange"}),a("span",{staticClass:"ngrm-icon-trash",on:{click:function(a){return e.removeItem(t)}}})])])})),0)])])},_e=[],we=(a("7f7f"),{name:"CropSizes",props:["translations","availableVariations","allVariationValues","imageSize","selectedVariation"],data:function(){return{newSelection:[],addingVariations:!1}},computed:{unselectedVariations:function(){var e=Object.keys(this.availableVariations),t=Object.keys(this.allVariationValues);return e.difference(t)},selectedVariations:function(){return Object.getOwnPropertyNames(this.allVariationValues)}},methods:{handleAddCropSize:function(){this.addingVariations=!0},handleCancel:function(){this.addingVariations=!1,this.newSelection=[]},handleAdd:function(){this.$emit("addedVariations",this.newSelection),this.newSelection=[],this.addingVariations=!1},removeItem:function(e){this.$emit("removedVariation",e)},formattedSize:function(e){return"".concat(this.availableVariations[e][0]," x ").concat(this.availableVariations[e][1])},isVariationSelectable:function(e){var t=Object(M["a"])(this.availableVariations[e],2),a=t[0],i=t[1];return this.imageSize.width>=a&&this.imageSize.height>=i},handleVariationClicked:function(e){this.isVariationSelectable(e)&&this.$emit("selected",e)}}}),ye=we,Ce=(a("bdd7"),Object(F["a"])(ye,be,_e,!1,null,"ad171770",null)),xe=Ce.exports,Oe=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"crop"},[a("div",{ref:"cropper",staticClass:"cropper",style:e.cropperStyle},[a("img",{ref:"image",attrs:{src:e.src}}),a("div",{ref:"buttons",staticClass:"buttons",style:e.applyButtonStyle},[a("button",{staticClass:"btn btn-blue",attrs:{type:"button"},on:{click:e.handleReset}},[a("span",{staticClass:"ngrm-icon-ccw"}),a("span",[e._v(e._s(this.translations.crop_reset))])]),a("button",{staticClass:"btn btn-blue",attrs:{type:"button"},on:{click:e.handleApply}},[a("span",{staticClass:"ngrm-icon-ok"}),a("span",[e._v(e._s(this.translations.crop_apply))])])])]),a("div",[a("h4",[e._v(e._s(this.translations.crop_preview))]),a("div",{ref:"preview",staticClass:"preview"})])])},Ie=[],Fe=a("5435"),Ve={name:"Crop",props:["translations","value","variation","src","imageSize"],mounted:function(){this.setCropper()},beforeDestroy:function(){this.destroyCropper()},data:function(){return{crop:{},cropper:null}},methods:{setCropper:function(){var e,t=this.value||{},a=t.x,i=t.y,n=t.w,s=t.h,o={x:a,y:i,width:n,height:s},r=Object(M["a"])(this.variation,2),l=r[0],c=r[1],d=l>0&&c>0?l/c:void 0;this.destroyCropper();this.$refs.cropper.clientWidth,this.imageSize.width;this.cropper=new Fe["a"](this.$refs.image,(e={viewMode:2,dragMode:"none",autoCrop:!0,data:o,aspectRatio:d,guides:!0,movable:!1,rotatable:!1},Object(f["a"])(e,"guides",!1),Object(f["a"])(e,"center",!1),Object(f["a"])(e,"zoomable",!1),Object(f["a"])(e,"scalable",!0),Object(f["a"])(e,"minCropBoxWidth",50),Object(f["a"])(e,"minCropBoxHeight",50),Object(f["a"])(e,"crop",this.handleCrop),Object(f["a"])(e,"preview",this.$refs.preview),e)),this.cropper.setData(o)},handleCrop:function(e){this.crop=this.cropper.getData(!0)},destroyCropper:function(){this.cropper&&this.cropper.destroy()},handleReset:function(){this.cropper.reset()},handleApply:function(){var e=this.cropper.getData(!0),t=e.x,a=e.y,i=e.width,n=e.height;this.$emit("change",{x:t,y:a,w:i,h:n})}},computed:{applyButtonStyle:function(){var e=this.crop,t=e.x,a=e.y,i=e.width,n=e.height,s=this.$refs.buttons?this.$refs.buttons.clientWidth:0,o=this.$refs.cropper?this.$refs.cropper.clientWidth/this.imageSize.width:1;return{top:"".concat((a+n)*o+10,"px"),left:"".concat((t+i)*o-s-1,"px")}},cropperStyle:function(){var e=this.imageSize.height/this.imageSize.width*100;return{"padding-bottom":"".concat(e,"%"),height:"0px",width:"100%"}}}},ke=Ve,je=(a("c66d"),Object(F["a"])(ke,Oe,Ie,!1,null,"5f73791e",null)),Se=je.exports,Me=function(e){return function(t){return Object.keys(t).reduce((function(a,i){return e(t[i],i)&&(a[i]=t[i]),a}),{})}},Te=function(e){return function(t){return!e(t)}},$e=function(e){return function(t){return e===t}},Ee=function(e){return!!e},Pe=Te($e(void 0));function ze(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function Re(e){for(var t=1;t1?a("v-select",{attrs:{options:e.visibilities,label:"name",reduce:function(e){return e.id},clearable:!1},model:{value:e.visibility,callback:function(t){e.visibility=t},expression:"visibility"}}):e._e(),a("input",{directives:[{name:"model",rawName:"v-model",value:e.overwrite,expression:"overwrite"}],attrs:{type:"checkbox",id:"ngrm-upload-overwrite"},domProps:{checked:Array.isArray(e.overwrite)?e._i(e.overwrite,null)>-1:e.overwrite},on:{change:function(t){var a=e.overwrite,i=t.target,n=!!i.checked;if(Array.isArray(a)){var s=null,o=e._i(a,s);i.checked?o<0&&(e.overwrite=a.concat([s])):o>-1&&(e.overwrite=a.slice(0,o).concat(a.slice(o+1)))}else e.overwrite=n}}}),a("label",{attrs:{for:"ngrm-upload-overwrite"}},[e._v(e._s(this.config.translations.upload_checkbox_overwrite))]),a("button",{staticClass:"btn btn-blue",attrs:{type:"button",disabled:""===e.filename||""===e.visibility},on:{click:e.upload}},[e._v("\n "+e._s(this.config.translations.upload_button_upload)+"\n ")])],1)],1),e.loading?a("i",{staticClass:"ng-icon ng-spinner"}):e._e()])},Be=[],qe=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"folder-gallery"},[a("div",{class:e.loading?"items loading":"items"},[a("div",{staticClass:"breadcrumbs"},[a("span",[e._v(e._s(this.config.translations.upload_breadcrumbs_info)+" ")]),e._l(e.breadcrumbs,(function(t,i){return a("span",{key:i},[0!==i?a("span",[e._v(" / ")]):e._e(),i!==e.breadcrumbs.length-1?a("a",{attrs:{href:"javascript:void(0);"},on:{click:function(a){return e.openFolder(t.id)}}},[e._v("\n "+e._s(t.label)+"\n ")]):a("span",[e._v(e._s(t.label))])])}))],2),e.folders.length>0||e.allowCreate?a("div",{staticClass:"info"},[a("i",{staticClass:"fa fa-info-circle"}),e._v("\n "+e._s(this.config.translations.upload_info_text)+"\n ")]):e._e(),e._l(e.folders,(function(t){return a("div",{key:t.id,staticClass:"media",class:{selected:t.id===e._self.folder}},[a("div",{staticClass:"media-container",on:{dblclick:function(a){return e.openFolder(t.id)}}},[e._m(0,!0),a("Label",{staticClass:"filename"},[e._v(e._s(t.label))])],1),a("button",{staticClass:"btn btn-blue select-btn",attrs:{type:"button"},on:{click:function(a){return e.$emit("select",t.id)}}},[e._v("\n "+e._s(e._self.config.translations.upload_button_select)+"\n ")])])})),e.allowCreate?a("div",{staticClass:"media new-folder"},[a("div",{staticClass:"media-container"},[e._m(1),a("input",{directives:[{name:"model",rawName:"v-model",value:e.newFolder,expression:"newFolder"}],attrs:{type:"text",placeholder:this.config.translations.upload_placeholder_new_folder},domProps:{value:e.newFolder},on:{input:function(t){t.target.composing||(e.newFolder=t.target.value)}}})]),a("button",{staticClass:"btn btn-blue select-btn",attrs:{type:"button",disabled:null===e.newFolder},on:{click:e.createNewFolder}},[e._v("\n "+e._s(this.config.translations.upload_button_create)+"\n ")])]):e._e()],2),e.loading?a("i",{staticClass:"ng-icon ng-spinner"}):e._e()])},Ge=[function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("span",{staticClass:"file-placeholder"},[a("span",{staticClass:"icon-doc"},[a("i",{staticClass:"fa fa-folder"})])])},function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("span",{staticClass:"file-placeholder"},[a("span",{staticClass:"icon-doc"},[a("i",{staticClass:"fa fa-folder"})])])}],We=a("bc3a"),Ye=a.n(We),Je={name:"SelectFolder",props:["config","selectedFolder"],data:function(){return{folders:[],newFolder:null,breadcrumbs:[],loading:!1,allowCreate:!0,folder:this.selectedFolder}},methods:{openFolder:function(e){this.folder=e,this.$emit("change",this.folder),this.loadSubFolders(e)},loadSubFolders:function(){var e=Object(d["a"])(regeneratorRuntime.mark((function e(t){var a,i,n;return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:return this.loading=!0,a=this.config.paths.load_folders,t&&(i={folder:t===A?"":t},a+="?"+g(i)),e.next=5,fetch(a);case 5:return n=e.sent,e.next=8,n.json();case 8:this.folders=e.sent,this.generateBreadcrumbs(t),this.newFolder=null,this.loading=!1;case 12:case"end":return e.stop()}}),e,this)})));function t(t){return e.apply(this,arguments)}return t}(),generateBreadcrumbs:function(e){var t=this;this.breadcrumbs=[];var a={id:null,label:this.config.translations.upload_root_folder};if(null!==e){var i=e.split("/"),n=[],s=[];this.config.parentFolder&&(s=this.config.parentFolder.id.split("/"),a={id:this.config.parentFolder.id,label:this.config.parentFolder.label}),this.config.folder&&(s=this.config.folder.id.split("/"),a={id:this.config.folder.id,label:this.config.folder.label}),this.breadcrumbs.push(a),i.forEach((function(e,a){n.push(e),s.indexOf(e)<0&&t.breadcrumbs.push({id:n.join("/"),label:e})}))}else this.breadcrumbs.push(a)},createNewFolder:function(){var e=Object(d["a"])(regeneratorRuntime.mark((function e(){var t;return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:return this.loading=!0,t=new FormData,this.folder&&t.append("parent",this.folder),t.append("folder",this.newFolder),e.next=6,Ye.a.post(this.config.paths.create_folder,t);case 6:this.folders.push({id:null!==this.folder?this.folder+"/"+this.newFolder:this.newFolder,label:this.newFolder}),this.newFolder=null,this.loading=!1;case 9:case"end":return e.stop()}}),e,this)})));function t(){return e.apply(this,arguments)}return t}()},created:function(){if(this.config.folder)return e=this.config.folder.id,this.allowCreate=!1,this.folder=e,this.$emit("change",this.folder),void this.generateBreadcrumbs(e);var e=null;this.config.parentFolder&&(e=this.config.parentFolder.id),this.openFolder(e)}},Ke=Je,Qe=(a("85ab"),Object(F["a"])(Ke,qe,Ge,!1,null,"c53732c8",null)),He=Qe.exports,Xe={name:"UploadModal",props:["config","file","visibilities"],data:function(){return{loading:!1,selectedFolder:"",filename:this.file.name,visibility:this.visibilities.length>0?this.visibilities[0].id:"",overwrite:!1,error:"",existingResourceButton:!1,existingResource:null}},components:{"select-folder":He,modal:le,"v-select":x.a},methods:{handleFolderChange:function(e){this.selectedFolder=e},upload:function(){var e=Object(d["a"])(regeneratorRuntime.mark((function e(){var t,a,i,n,s,o,r=this;return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:for(this.loading=!0,t=new FormData,t.append("file",this.file),t.append("filename",this.filename),t.append("folder",this.selectedFolder),t.append("overwrite",this.overwrite),t.append("visibility",this.visibility),t.append("hide_filename",this.config.hideFilename),a=0,i=Object.entries(this.config.uploadContext);a0&&-1===r.config.allowedTypes.indexOf(e.data.type)?(r.error=r.config.translations.upload_error_unsupported_resource_type+r.config.allowedTypes.join(", "),r.loading=!1):r.$emit("uploaded",e.data)})).catch((function(e){409===e.response.status?(r.error=r.config.translations.upload_error_existing_resource,r.existingResourceButton=!0,r.existingResource=e.response.data,r.loading=!1):(r.error=e.response.data.detail?e.response.data.detail:"Error "+e.response.status+" - "+e.response.statusText,r.loading=!1)}));case 11:case"end":return e.stop()}}),e,this)})));function t(){return e.apply(this,arguments)}return t}()},watch:{visibilities:function(){this.visibility=this.visibilities.length>0?this.visibilities[0].id:""}}},Ze=Xe,et=(a("5af5"),Object(F["a"])(Ze,Ne,Be,!1,null,"2676fd5e",null)),tt=et.exports;function at(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function it(e){for(var t=1;t0},stringifiedVariations:function(){return JSON.stringify(Me(Ee)(this.selectedImage.variations))}},data:function(){return{mediaModalOpen:!1,cropModalOpen:!1,uploadModalOpen:!1,types:[],folders:[],tags:[],visibilities:[],facetsLoading:!0,newFile:null}},methods:{dispatchVanillaChangeEvent:function(){this.$nextTick((function(){this.$el.dispatchEvent(new CustomEvent("ngrm-change",{detail:{inputFields:this.config.inputFields,selectedImage:this.selectedImage,fieldId:this.fieldId}}))}))},prepareDomForModal:function(){var e=document.querySelector(".ez-page-builder-wrapper");e&&(e.style.transform="none")},resetDomAfterModal:function(){var e=document.querySelector(".ez-page-builder-wrapper");e&&e.removeAttribute("style")},handleMediaModalClose:function(){this.mediaModalOpen=!1,this.resetDomAfterModal(),this.dispatchVanillaChangeEvent()},handleCropModalClose:function(){this.cropModalOpen=!1,this.resetDomAfterModal(),this.dispatchVanillaChangeEvent()},handleUploadModalClose:function(){this.uploadModalOpen=!1,this.dispatchVanillaChangeEvent()},handleMediaSelected:function(e){this.selectedImage={id:e.remoteId,name:e.filename,type:e.type,format:e.format,url:e.url,previewUrl:e.previewUrl,browseUrl:e.browseUrl,alternateText:e.altText,caption:e.caption,watermarkText:this.selectedImage.watermarkText,tags:e.tags,size:e.size,variations:{},height:e.height,width:e.width},this.mediaModalOpen=!1,this.dispatchVanillaChangeEvent()},handleVariationCropChange:function(e){this.selectedImage=it({},this.selectedImage,{variations:it({},this.selectedImage.variations,{},e)}),this.dispatchVanillaChangeEvent()},handleResourceUploaded:function(e){this.selectedImage={id:e.remoteId,name:e.filename,type:e.type,format:e.format,url:e.url,previewUrl:e.previewUrl,browseUrl:e.browseUrl,alternateText:e.altText,caption:e.caption,watermarkText:this.selectedImage.watermarkText,tags:e.tags,size:e.size,variations:{},height:e.height,width:e.width},this.uploadModalOpen=!1,this.dispatchVanillaChangeEvent()},handleCropClicked:function(){this.cropModalOpen=!0,this.prepareDomForModal()},handleRemoveMediaClicked:function(){this.selectedImage={id:"",name:"",type:"image",format:"",url:"",previewUrl:"",browseUrl:"",alternateText:"",caption:"",watermarkText:this.selectedImage.watermarkText,tags:[],size:0,variations:{},height:0,width:0},this.$refs.fileUploadInput.value=null,this.dispatchVanillaChangeEvent()},fetchFacets:function(){var e=Object(d["a"])(regeneratorRuntime.mark((function e(){var t,a,i=this;return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:return e.next=2,fetch(this.config.paths.load_facets);case 2:return t=e.sent,e.next=5,t.json();case 5:a=e.sent,this.types=[],this.tags=[],this.visibilities=[],a.types.forEach((function(e){-1===i.config.allowedTypes.indexOf(e.id)&&0!==i.config.allowedTypes.length||i.types.push(e)})),a.tags.forEach((function(e){-1===i.config.allowedTags.indexOf(e.id)&&0!==i.config.allowedTags.length||i.tags.push(e)})),a.visibilities.forEach((function(e){-1===i.config.allowedVisibilities.indexOf(e.id)&&0!==i.config.allowedVisibilities.length||i.visibilities.push(e)})),this.facetsLoading=!1;case 13:case"end":return e.stop()}}),e,this)})));function t(){return e.apply(this,arguments)}return t}(),handleBrowseMediaClicked:function(){var e=Object(d["a"])(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:this.mediaModalOpen=!0,this.prepareDomForModal(),this.fetchFacets();case 3:case"end":return e.stop()}}),e,this)})));function t(){return e.apply(this,arguments)}return t}(),handleFileInputChange:function(){this.fetchFacets(),this.uploadModalOpen=!0,this.newFile=this.$refs.fileUploadInput.files.item(0)}},watch:{selectedImage:function(){this.$emit("selectedImageChanged",this.selectedImage)}},mounted:function(){this.$nextTick((function(){var e=document.querySelector(".ngrm-model-portal-".concat(this.fieldId));document.body.prepend(e)}))}},st=nt,ot=Object(F["a"])(st,l,c,!1,null,null,null),rt=ot.exports,lt={bind:function(e,t,a){var i=b(t.arg);a.context[i]=t.value}},ct=(a("b39d"),function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",[a("input",{directives:[{name:"model",rawName:"v-model",value:e.selectedFolder,expression:"selectedFolder"}],attrs:{type:"hidden",name:e.config.inputFields.folder},domProps:{value:e.selectedFolder},on:{input:function(t){t.target.composing||(e.selectedFolder=t.target.value)}}}),e.selectedFolder?a("span",[a("i",{staticClass:"fa fa-folder"}),e._v(" "+e._s(this.selectedFolder))]):a("span",[a("i",[e._v(e._s(this.config.translations.select_folder_interaction_empty))])]),a("div",{staticClass:"ngremotemedia-buttons"},[e.selectedFolder?a("input",{attrs:{type:"button",value:"Remove folder"},on:{click:e.handleFolderRemove}}):e._e(),a("input",{attrs:{type:"button",value:this.config.translations.select_folder_interaction_button},on:{click:e.handleSelectFolderModalOpen}})]),e.selectFolderModalOpen?a("modal",{attrs:{title:"Select folder"},on:{close:e.handleSelectFolderModalClose}},[a("select-folder",{attrs:{config:e.config,"selected-folder":e.selectedFolder},on:{select:e.handleFolderSelect}})],1):e._e()],1)}),dt=[],ft={name:"SelectFolderInteraction",props:["config","selectedFolder"],components:{"select-folder":He,modal:le},data:function(){return{selectFolderModalOpen:!1}},methods:{handleSelectFolderModalOpen:function(){this.selectFolderModalOpen=!0},handleSelectFolderModalClose:function(){this.selectFolderModalOpen=!1},handleFolderSelect:function(e){this.selectedFolder=e,this.handleSelectFolderModalClose()},handleFolderRemove:function(){this.selectedFolder=null}}},ut=ft,pt=Object(F["a"])(ut,ct,dt,!1,null,null,null),ht=pt.exports;i["default"].config.productionTip=!1,i["default"].use(s.a),i["default"].use(r.a);var mt=function(e){window["ngrm_interactions_vue_".concat(e.dataset.id)]=new i["default"]({el:e,directives:{init:lt},data:{config:{paths:{browse_resources:"/resource/browse",upload_resources:"/resource/upload",load_facets:"/facets/load",load_folders:"/folder/load",create_folder:"/folder/create"},translations:{},inputFields:{locationId:"locationId",remoteId:"remoteId",type:"type",altText:"altText",caption:"caption",tags:"tags[]",cropSettings:"cropSettings",source:"source",watermarkText:"watermarkText"},availableVariations:[],allowedVisibilities:[],allowedTypes:[],allowedTags:[],parentFolder:null,folder:null,uploadContext:{},disableUpload:!1,hideFilename:!1},selectedImage:{id:"",name:"",type:"image",format:"",url:"",browse_url:"",previewUrl:"",alternateText:"",caption:"",watermarkText:"",tags:[],size:"",variations:{},height:0,width:0}},components:{interactions:rt}})},gt=function(e){window["ngrm_select_folder_vue_".concat(e.dataset.id)]=new i["default"]({el:e,directives:{init:lt},data:{config:{paths:{load_folders:"/folder/load",create_folder:"/folder/create"},translations:{},inputFields:{folder:"folder"}},selectedFolder:null},components:{"select-folder-interaction":ht}})},vt=function(){for(var e=document.getElementsByClassName("ngremotemedia-container"),t=0;t0?e.config.allowedTags:e.allTags,multiple:"",taggable:0===e.config.allowedTags.length},on:{input:e.handleTagsInput},model:{value:e.selectedImage.tags,callback:function(t){e.$set(e.selectedImage,"tags",t)},expression:"selectedImage.tags"}}),a("select",{directives:[{name:"model",rawName:"v-model",value:e.selectedImage.tags,expression:"selectedImage.tags"}],staticClass:"ngremotemedia-newtags",attrs:{hidden:"",name:this.config.inputFields.tags,multiple:"multiple"},on:{change:function(t){var a=Array.prototype.filter.call(t.target.options,(function(e){return e.selected})).map((function(e){var t="_value"in e?e._value:e.value;return t}));e.$set(e.selectedImage,"tags",t.target.multiple?a:a[0])}}},e._l(e.allTags,(function(t){return a("option",{key:t},[e._v(e._s(t))])})),0)],1),a("div",{staticClass:"ngremotemedia-watermark-text"},[a("span",{staticClass:"help-block description"},[e._v("\n "+e._s(this.config.translations.preview_watermark_text)+"\n "),a("i",{staticClass:"fa fa-info-circle",attrs:{"data-toggle":"tooltip","data-placement":"right",title:this.config.translations.preview_watermark_text_info}})]),a("input",{directives:[{name:"model",rawName:"v-model",value:e.selectedImage.watermarkText,expression:"selectedImage.watermarkText"},{name:"debounce",rawName:"v-debounce:500ms",value:e.dispatchChangeEvent,expression:"dispatchChangeEvent",arg:"500ms"}],staticClass:"media-watermarktext data",attrs:{type:"text",name:this.config.inputFields.watermarkText},domProps:{value:e.selectedImage.watermarkText},on:{input:function(t){t.target.composing||e.$set(e.selectedImage,"watermarkText",t.target.value)}}})])])])]):a("div",[a("i",[e._v(e._s(this.config.translations.interactions_no_media_selected))])])}),p=[],h=(a("5df3"),a("4f7f"),a("75fc")),m=(a("28a5"),a("01c8")),g=function(e){var t=[];for(var a in e)t.push(encodeURIComponent(a)+"="+encodeURIComponent(e[a]));return t.join("&")},v=function(e){return e[0].toUpperCase()+e.slice(1)},b=function(e){var t=e.split("-"),a=Object(m["a"])(t),i=a[0],n=a.slice(1);return[i].concat(Object(h["a"])(n.map(v))).join("")},_=function(e,t){var a=Math.pow(10,t);return parseFloat(Math.round(e*a)/a).toFixed(t)},w={B:"KB",KB:"MB",MB:"GB",GB:"TB"},y=function e(t){var a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"B",i=w[a];return!i||t<1024?"".concat(_(t,2)," ").concat(a):e(t/1024,i)},C=a("4a7a"),x=a.n(C),O={name:"Preview",props:["config","fieldId","selectedImage"],data:function(){return{allTags:[]}},components:{"v-select":x.a},computed:{nonImagePreviewClass:function(){return"video"===this.selectedImage.type?"ng-video":"ng-book"},formattedSize:function(){return y(this.selectedImage.size)}},methods:{handleTagsInput:function(e){this.allTags=Object(h["a"])(new Set([].concat(Object(h["a"])(this.allTags),Object(h["a"])(e)))),this.dispatchChangeEvent()},dispatchChangeEvent:function(){this.$emit("preview-change")}},mounted:function(){this.allTags=Object(h["a"])(this.selectedImage.tags)},watch:{selectedImage:function(){this.allTags=Object(h["a"])(this.selectedImage.tags)}}},I=O,F=(a("6b2c"),a("2877")),V=Object(F["a"])(I,u,p,!1,null,"77f74934",null),k=V.exports,j=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("modal",{attrs:{title:this.config.translations.browse_title},on:{close:function(t){return e.$emit("close")}}},[a("media-facets",{attrs:{config:e.config,tags:e.tags,types:e.types,visibilities:e.visibilities,facets:e.facets,"facets-loading":e.facetsLoading},on:{change:e.handleFacetsChange}}),a("media-gallery",{attrs:{translations:e.config.translations,media:e.media,canLoadMore:e.canLoadMore,selectedMediaId:e.selectedMediaId,loading:e.loading},on:{loadMore:e.handleLoadMore,"media-selected":function(t){return e.$emit("media-selected",t)}}}),e.loading?a("i",{staticClass:"ng-icon ng-spinner"}):e._e()],1)},S=[],M=a("768b"),T=(a("ffc1"),function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"mediaFacets"},[a("div",{staticClass:"body"},[e.types.length>1?a("div",{staticClass:"form-field"},[a("label",{attrs:{for:"type"}},[e._v(e._s(this.config.translations.browse_select_type))]),a("v-select",{attrs:{options:e.types,label:"name",reduce:function(e){return e.id},placeholder:e.facetsLoading?this.config.translations.browse_loading_types:this.config.translations.browse_all_types},on:{input:e.handleTypeChange},model:{value:e.selectedType,callback:function(t){e.selectedType=t},expression:"selectedType"}})],1):e._e(),e.folders&&!this.config.folder?a("div",{staticClass:"form-field"},[a("label",{attrs:{for:"folder"}},[e._v(e._s(this.config.translations.browse_select_folder))]),a("treeselect",{attrs:{multiple:!1,options:e.folders,"load-options":e.loadSubFolders,value:this.config.parentFolder?this.config.parentFolder.id:"",placeholder:e.facetsLoading?this.config.translations.browse_loading_folders:this.config.translations.browse_all_folders,disabled:e.facetsLoading,clearable:!0,beforeClearAll:e.clearFolderField,defaultExpandLevel:1},on:{input:e.handleFolderChange},model:{value:e.selectedFolder,callback:function(t){e.selectedFolder=t},expression:"selectedFolder"}})],1):e._e(),e.tags.length>1?a("div",{staticClass:"form-field"},[a("label",{attrs:{for:"tag"}},[e._v(e._s(this.config.translations.browse_select_tag))]),a("v-select",{attrs:{options:e.tags,label:"name",reduce:function(e){return e.id},placeholder:e.facetsLoading?this.config.translations.browse_loading_tags:this.config.translations.browse_all_tags,disabled:e.facetsLoading},on:{input:e.handleTagChange},model:{value:e.tag,callback:function(t){e.tag=t},expression:"tag"}})],1):e._e(),e.visibilities.length>1?a("div",{staticClass:"form-field"},[a("label",{attrs:{for:"visibilities"}},[e._v(e._s(this.config.translations.browse_select_visibility))]),a("v-select",{attrs:{options:e.visibilities,label:"name",reduce:function(e){return e.id},placeholder:e.facetsLoading?this.config.translations.browse_loading_visibilities:this.config.translations.browse_all_visibilities,disabled:e.facetsLoading},on:{input:e.handleVisibilityChange},model:{value:e.visibility,callback:function(t){e.visibility=t},expression:"visibility"}})],1):e._e(),a("div",{staticClass:"search-wrapper"},[a("span",{staticClass:"search-label"},[e._v(e._s(this.config.translations.search))]),a("div",{staticClass:"search"},[a("input",{directives:[{name:"model",rawName:"v-model",value:e.query,expression:"query"}],attrs:{type:"text",placeholder:this.config.translations.search_placeholder},domProps:{value:e.query},on:{keyup:e.handleQueryChange,keydown:function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")?null:(t.preventDefault(),null(t))},input:function(t){t.target.composing||(e.query=t.target.value)}}})])])])])}),$=[],E="all",P="image",z="video",R="raw",L="(all)",A="(root)",U="all",D=a("ca17"),N=a.n(D),B=(a("542c"),{name:"MediaFacets",props:["config","tags","types","visibilities","facets","facetsLoading"],data:function(){return{TYPE_ALL:E,TYPE_IMAGE:P,TYPE_VIDEO:z,TYPE_RAW:R,FOLDER_ALL:L,FOLDER_ROOT:A,TAG_ALL:U,folders:[{id:this.config.parentFolder?this.config.parentFolder.id:A,label:this.config.parentFolder?this.config.parentFolder.label:A,children:null}],selectedFolder:this.facets.folder,selectedType:this.facets.type,query:this.facets.query,tag:this.facets.tag,visibility:this.facets.visibility}},methods:{clearFolderField:function(){return!this.config.parentFolder||(this.selectedFolder=this.config.parentFolder.id,!1)},handleTypeChange:function(e){this.$emit("change",{type:e})},handleFolderChange:function(e){this.selectedFolder=e,"undefined"!==typeof e&&e||(this.selectedFolder=this.config.parentFolder?this.config.parentFolder.id:e),this.$emit("change",{folder:this.selectedFolder})},handleQueryChange:function(){this.$emit("change",{query:this.query})},handleTagChange:function(){this.$emit("change",{tag:this.tag})},handleVisibilityChange:function(){this.$emit("change",{visibility:this.visibility})},loadSubFolders:function(){var e=Object(d["a"])(regeneratorRuntime.mark((function e(t){var a,i,n;return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:return a=t.parentNode,i={folder:"(root)"===a.id?"":a.id},e.next=4,fetch(this.config.paths.load_folders+"?"+g(i));case 4:return n=e.sent,e.next=7,n.json();case 7:a.children=e.sent,t.callback();case 9:case"end":return e.stop()}}),e,this)})));function t(t){return e.apply(this,arguments)}return t}()},components:{"v-select":x.a,treeselect:N.a}}),q=B,G=(a("c345"),Object(F["a"])(q,T,$,!1,null,"278bb214",null)),W=G.exports,Y=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"media-gallery"},[a("div",{class:e.loading?"items loading":"items"},[e.media.length?e._e():a("div",{staticClass:"folder-empty"},[a("span",{staticClass:"ngrm-icon-folder"}),a("span",[a("strong",[e._v(e._s(this.translations.media_gallery_empty_folder))]),e._v(e._s(this.translations.media_gallery_upload_media))])]),e._l(e.media,(function(t){return a("div",{key:t.id,staticClass:"media",class:{selected:t.remoteId===e.selectedMediaId}},["image"===t.type||"video"===t.type&&""!==t.browseUrl?a("div",{staticClass:"media-container"},[a("img",{staticClass:"img",attrs:{src:t.browseUrl,alt:t.filename}}),a("Label",{staticClass:"filename"},[e._v(e._s(t.filename))]),a("div",{staticClass:"size-description"},["public"===t.visibility?a("i",{staticClass:"fa fa-solid fa-globe"},[e._v("  ")]):e._e(),"private"===t.visibility?a("i",{staticClass:"fa fa-eye-slash"},[e._v("  ")]):e._e(),"protected"===t.visibility?a("i",{staticClass:"fa fa-lock"},[e._v("  ")]):e._e(),a("span",{staticClass:"format"},[e._v(e._s(t.format))]),e._v(" - "+e._s(t.width)+" x "+e._s(t.height)+" - "+e._s(e.showFilesize(t))+"\n ")])],1):a("div",{staticClass:"media-container"},[a("span",{staticClass:"file-placeholder"},[a("span",{staticClass:"icon-doc"},["pdf"===t.format?a("i",{staticClass:"fa fa-file-pdf-o"}):"zip"===t.format||"rar"===t.format?a("i",{staticClass:"fa fa-file-archive-o"}):"ppt"===t.format||"pptx"===t.format?a("i",{staticClass:"fa fa-file-powerpoint-o"}):"doc"===t.format||"docx"===t.format?a("i",{staticClass:"fa fa-file-word-o"}):"xls"===t.format||"xlsx"===t.format?a("i",{staticClass:"fa fa-file-excel-o"}):"aac"===t.format||"aiff"===t.format||"amr"===t.format||"flac"===t.format||"m4a"===t.format||"mp3"===t.format||"ogg"===t.format||"opus"===t.format||"wav"===t.format?a("i",{staticClass:"fa fa-file-audio-o"}):"txt"===t.format?a("i",{staticClass:"fa fa-lg fa-file-text"}):a("i",{staticClass:"fa fa-file"})])]),a("Label",{staticClass:"filename"},[e._v(e._s(t.filename))]),a("div",{staticClass:"size-description"},["public"===t.visibility?a("i",{staticClass:"fa fa-solid fa-globe"},[e._v("  ")]):e._e(),"private"===t.visibility?a("i",{staticClass:"fa fa-eye-slash"},[e._v("  ")]):e._e(),"protected"===t.visibility?a("i",{staticClass:"fa fa-lock"},[e._v("  ")]):e._e(),a("span",{staticClass:"format"},[e._v(e._s(t.format))]),e._v(" - "+e._s(e.showFilesize(t))+"\n ")])],1),a("button",{staticClass:"btn btn-blue select-btn",attrs:{type:"button"},on:{click:function(a){return e.$emit("media-selected",t)}}},[e._v(e._s(e._self.translations.media_gallery_select))])])}))],2),e.canLoadMore?a("div",{staticClass:"load-more-wrapper"},[a("button",{staticClass:"btn btn-blue",attrs:{type:"button"},on:{click:function(t){return e.$emit("loadMore")}}},[e._v(e._s(this.translations.media_gallery_load_more))])]):e._e()])},J=[],K=a("94df"),Q=a.n(K),H={name:"MediaGallery",props:["translations","media","canLoadMore","onLoadMore","selectedMediaId","loading"],methods:{showFilesize:function(e){return Q()(e.size)}}},X=H,Z=(a("bdf2"),Object(F["a"])(X,Y,J,!1,null,"5aa52c90",null)),ee=Z.exports,te=a("b012"),ae=a.n(te),ie=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"ngrm-overlay"},[a("div",{staticClass:"media-modal"},[a("div",{staticClass:"title"},[e._v("\n "+e._s(e.title)+"\n "),a("span",{staticClass:"close",on:{click:e.close}},[a("span",{staticClass:"ngrm-icon-cancel"})])]),a("div",{staticClass:"body"},[e._t("default")],2)])])},ne=[],se={name:"Modal",props:["title"],methods:{close:function(){this.$emit("close")}}},oe=se,re=(a("11ef"),a("a301"),Object(F["a"])(oe,ie,ne,!1,null,"9d33d07a",null)),le=re.exports;function ce(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function de(e){for(var t=1;t0&&void 0!==p[0]?p[0]:{patch:!1},a=t.patch,this.loading=!0,this.abortController&&this.abortController.abort(),this.abortController=new AbortController,i={limit:fe,offset:a?this.media.length:0},this.facets.query&&(i["query"]=this.facets.query),this.config.allowedTypes.length>0&&(i["type"]=this.config.allowedTypes),this.facets.type&&(i["type"]=this.facets.type),this.facets.folder&&(i["folder"]=this.facets.folder===A?"":this.facets.folder),this.config.allowedTags.length>0&&(i["tag"]=this.config.allowedTags),this.facets.tag&&(i["tag"]=this.facets.tag),this.config.allowedVisibilities.length>0&&(i["visibility"]=this.config.allowedVisibilities),this.facets.visibility&&(i["visibility"]=this.facets.visibility),a&&this.nextCursor&&(i["next_cursor"]=this.nextCursor),n="",s=0,o=Object.entries(i);s-1:e.newSelection},on:{change:function(a){var i=e.newSelection,n=a.target,s=!!n.checked;if(Array.isArray(i)){var o=t,r=e._i(i,o);n.checked?r<0&&(e.newSelection=i.concat([o])):r>-1&&(e.newSelection=i.slice(0,r).concat(i.slice(r+1)))}else e.newSelection=s}}}),a("label",{attrs:{for:t}},[a("span",{staticClass:"name"},[e._v(e._s(t))]),a("span",{staticClass:"formatted-size"},[e._v(e._s(e.formattedSize(t)))])]),e.isVariationSelectable(t)?e._e():a("div",{staticClass:"legend-not-selectable"},[a("span",[e._v(e._s(e._self.translations.crop_media_too_small))])])])})),0),a("div",{staticClass:"selectedVariations"},[a("ul",e._l(e.selectedVariations,(function(t){return a("li",{key:t,class:{set:!!e.allVariationValues[t],selected:e.selectedVariation===t,disabled:!e.isVariationSelectable(t)},on:{click:function(a){return e.handleVariationClicked(t)}}},[a("div",[a("span",{staticClass:"name"},[e._v(e._s(t))]),a("span",{staticClass:"formatted-size"},[e._v(e._s(e.formattedSize(t)))])]),e.addingVariations?e._e():a("a",[a("span",{staticClass:"circle-orange"}),a("span",{staticClass:"ngrm-icon-trash",on:{click:function(a){return e.removeItem(t)}}})])])})),0)])])},_e=[],we=(a("7f7f"),{name:"CropSizes",props:["translations","availableVariations","allVariationValues","imageSize","selectedVariation"],data:function(){return{newSelection:[],addingVariations:!1}},computed:{unselectedVariations:function(){var e=Object.keys(this.availableVariations),t=Object.keys(this.allVariationValues);return e.difference(t)},selectedVariations:function(){return Object.getOwnPropertyNames(this.allVariationValues)}},methods:{handleAddCropSize:function(){this.addingVariations=!0},handleCancel:function(){this.addingVariations=!1,this.newSelection=[]},handleAdd:function(){this.$emit("addedVariations",this.newSelection),this.newSelection=[],this.addingVariations=!1},removeItem:function(e){this.$emit("removedVariation",e)},formattedSize:function(e){return"".concat(this.availableVariations[e][0]," x ").concat(this.availableVariations[e][1])},isVariationSelectable:function(e){var t=Object(M["a"])(this.availableVariations[e],2),a=t[0],i=t[1];return this.imageSize.width>=a&&this.imageSize.height>=i},handleVariationClicked:function(e){this.isVariationSelectable(e)&&this.$emit("selected",e)}}}),ye=we,Ce=(a("bdd7"),Object(F["a"])(ye,be,_e,!1,null,"ad171770",null)),xe=Ce.exports,Oe=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"crop"},[a("div",{ref:"cropper",staticClass:"cropper",style:e.cropperStyle},[a("img",{ref:"image",attrs:{src:e.src}}),a("div",{ref:"buttons",staticClass:"buttons",style:e.applyButtonStyle},[a("button",{staticClass:"btn btn-blue",attrs:{type:"button"},on:{click:e.handleReset}},[a("span",{staticClass:"ngrm-icon-ccw"}),a("span",[e._v(e._s(this.translations.crop_reset))])]),a("button",{staticClass:"btn btn-blue",attrs:{type:"button"},on:{click:e.handleApply}},[a("span",{staticClass:"ngrm-icon-ok"}),a("span",[e._v(e._s(this.translations.crop_apply))])])])]),a("div",[a("h4",[e._v(e._s(this.translations.crop_preview))]),a("div",{ref:"preview",staticClass:"preview"})])])},Ie=[],Fe=a("5435"),Ve={name:"Crop",props:["translations","value","variation","src","imageSize"],mounted:function(){this.setCropper()},beforeDestroy:function(){this.destroyCropper()},data:function(){return{crop:{},cropper:null}},methods:{setCropper:function(){var e,t=this.value||{},a=t.x,i=t.y,n=t.w,s=t.h,o={x:a,y:i,width:n,height:s},r=Object(M["a"])(this.variation,2),l=r[0],c=r[1],d=l>0&&c>0?l/c:void 0;this.destroyCropper();this.$refs.cropper.clientWidth,this.imageSize.width;this.cropper=new Fe["a"](this.$refs.image,(e={viewMode:2,dragMode:"none",autoCrop:!0,data:o,aspectRatio:d,guides:!0,movable:!1,rotatable:!1},Object(f["a"])(e,"guides",!1),Object(f["a"])(e,"center",!1),Object(f["a"])(e,"zoomable",!1),Object(f["a"])(e,"scalable",!0),Object(f["a"])(e,"minCropBoxWidth",50),Object(f["a"])(e,"minCropBoxHeight",50),Object(f["a"])(e,"crop",this.handleCrop),Object(f["a"])(e,"preview",this.$refs.preview),e)),this.cropper.setData(o)},handleCrop:function(e){this.crop=this.cropper.getData(!0)},destroyCropper:function(){this.cropper&&this.cropper.destroy()},handleReset:function(){this.cropper.reset()},handleApply:function(){var e=this.cropper.getData(!0),t=e.x,a=e.y,i=e.width,n=e.height;this.$emit("change",{x:t,y:a,w:i,h:n})}},computed:{applyButtonStyle:function(){var e=this.crop,t=e.x,a=e.y,i=e.width,n=e.height,s=this.$refs.buttons?this.$refs.buttons.clientWidth:0,o=this.$refs.cropper?this.$refs.cropper.clientWidth/this.imageSize.width:1;return{top:"".concat((a+n)*o+10,"px"),left:"".concat((t+i)*o-s-1,"px")}},cropperStyle:function(){var e=this.imageSize.height/this.imageSize.width*100;return{"padding-bottom":"".concat(e,"%"),height:"0px",width:"100%"}}}},ke=Ve,je=(a("c66d"),Object(F["a"])(ke,Oe,Ie,!1,null,"5f73791e",null)),Se=je.exports,Me=function(e){return function(t){return Object.keys(t).reduce((function(a,i){return e(t[i],i)&&(a[i]=t[i]),a}),{})}},Te=function(e){return function(t){return!e(t)}},$e=function(e){return function(t){return e===t}},Ee=function(e){return!!e},Pe=Te($e(void 0));function ze(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function Re(e){for(var t=1;t1?a("v-select",{attrs:{options:e.visibilities,label:"name",reduce:function(e){return e.id},clearable:!1},model:{value:e.visibility,callback:function(t){e.visibility=t},expression:"visibility"}}):e._e(),a("input",{directives:[{name:"model",rawName:"v-model",value:e.overwrite,expression:"overwrite"}],attrs:{type:"checkbox",id:"ngrm-upload-overwrite"},domProps:{checked:Array.isArray(e.overwrite)?e._i(e.overwrite,null)>-1:e.overwrite},on:{change:function(t){var a=e.overwrite,i=t.target,n=!!i.checked;if(Array.isArray(a)){var s=null,o=e._i(a,s);i.checked?o<0&&(e.overwrite=a.concat([s])):o>-1&&(e.overwrite=a.slice(0,o).concat(a.slice(o+1)))}else e.overwrite=n}}}),a("label",{attrs:{for:"ngrm-upload-overwrite"}},[e._v(e._s(this.config.translations.upload_checkbox_overwrite))]),a("button",{staticClass:"btn btn-blue",attrs:{type:"button",disabled:""===e.filename||""===e.visibility},on:{click:e.upload}},[e._v("\n "+e._s(this.config.translations.upload_button_upload)+"\n ")])],1)],1),e.loading?a("i",{staticClass:"ng-icon ng-spinner"}):e._e()])},Be=[],qe=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"folder-gallery"},[a("div",{class:e.loading?"items loading":"items"},[a("div",{staticClass:"breadcrumbs"},[a("span",[e._v(e._s(this.config.translations.upload_breadcrumbs_info)+" ")]),e._l(e.breadcrumbs,(function(t,i){return a("span",{key:i},[0!==i?a("span",[e._v(" / ")]):e._e(),i!==e.breadcrumbs.length-1?a("a",{attrs:{href:"javascript:void(0);"},on:{click:function(a){return e.openFolder(t.id)}}},[e._v("\n "+e._s(t.label)+"\n ")]):a("span",[e._v(e._s(t.label))])])}))],2),e.folders.length>0||e.allowCreate?a("div",{staticClass:"info"},[a("i",{staticClass:"fa fa-info-circle"}),e._v("\n "+e._s(this.config.translations.upload_info_text)+"\n ")]):e._e(),e._l(e.folders,(function(t){return a("div",{key:t.id,staticClass:"media",class:{selected:t.id===e._self.folder}},[a("div",{staticClass:"media-container",on:{dblclick:function(a){return e.openFolder(t.id)}}},[e._m(0,!0),a("Label",{staticClass:"filename"},[e._v(e._s(t.label))])],1),a("button",{staticClass:"btn btn-blue select-btn",attrs:{type:"button"},on:{click:function(a){return e.$emit("select",t.id)}}},[e._v("\n "+e._s(e._self.config.translations.upload_button_select)+"\n ")])])})),e.allowCreate?a("div",{staticClass:"media new-folder"},[a("div",{staticClass:"media-container"},[e._m(1),a("input",{directives:[{name:"model",rawName:"v-model",value:e.newFolder,expression:"newFolder"}],attrs:{type:"text",placeholder:this.config.translations.upload_placeholder_new_folder},domProps:{value:e.newFolder},on:{input:function(t){t.target.composing||(e.newFolder=t.target.value)}}})]),a("button",{staticClass:"btn btn-blue select-btn",attrs:{type:"button",disabled:null===e.newFolder},on:{click:e.createNewFolder}},[e._v("\n "+e._s(this.config.translations.upload_button_create)+"\n ")])]):e._e()],2),e.loading?a("i",{staticClass:"ng-icon ng-spinner"}):e._e()])},Ge=[function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("span",{staticClass:"file-placeholder"},[a("span",{staticClass:"icon-doc"},[a("i",{staticClass:"fa fa-folder"})])])},function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("span",{staticClass:"file-placeholder"},[a("span",{staticClass:"icon-doc"},[a("i",{staticClass:"fa fa-folder"})])])}],We=a("bc3a"),Ye=a.n(We),Je={name:"SelectFolder",props:["config","selectedFolder"],data:function(){return{folders:[],newFolder:null,breadcrumbs:[],loading:!1,allowCreate:!0,folder:this.selectedFolder}},methods:{openFolder:function(e){this.folder=e,this.$emit("change",this.folder),this.loadSubFolders(e)},loadSubFolders:function(){var e=Object(d["a"])(regeneratorRuntime.mark((function e(t){var a,i,n;return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:return this.loading=!0,a=this.config.paths.load_folders,t&&(i={folder:t===A?"":t},a+="?"+g(i)),e.next=5,fetch(a);case 5:return n=e.sent,e.next=8,n.json();case 8:this.folders=e.sent,this.generateBreadcrumbs(t),this.newFolder=null,this.loading=!1;case 12:case"end":return e.stop()}}),e,this)})));function t(t){return e.apply(this,arguments)}return t}(),generateBreadcrumbs:function(e){var t=this;this.breadcrumbs=[];var a={id:null,label:this.config.translations.upload_root_folder};if(null!==e){var i=e.split("/"),n=[],s=[];this.config.parentFolder&&(s=this.config.parentFolder.id.split("/"),a={id:this.config.parentFolder.id,label:this.config.parentFolder.label}),this.config.folder&&(s=this.config.folder.id.split("/"),a={id:this.config.folder.id,label:this.config.folder.label}),this.breadcrumbs.push(a),i.forEach((function(e,a){n.push(e),s.indexOf(e)<0&&t.breadcrumbs.push({id:n.join("/"),label:e})}))}else this.breadcrumbs.push(a)},createNewFolder:function(){var e=Object(d["a"])(regeneratorRuntime.mark((function e(){var t;return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:return this.loading=!0,t=new FormData,this.folder&&t.append("parent",this.folder),t.append("folder",this.newFolder),e.next=6,Ye.a.post(this.config.paths.create_folder,t);case 6:this.folders.push({id:null!==this.folder?this.folder+"/"+this.newFolder:this.newFolder,label:this.newFolder}),this.newFolder=null,this.loading=!1;case 9:case"end":return e.stop()}}),e,this)})));function t(){return e.apply(this,arguments)}return t}()},created:function(){if(this.config.folder)return e=this.config.folder.id,this.allowCreate=!1,this.folder=e,this.$emit("change",this.folder),void this.generateBreadcrumbs(e);var e=null;this.config.parentFolder&&(e=this.config.parentFolder.id),this.openFolder(e)}},Ke=Je,Qe=(a("85ab"),Object(F["a"])(Ke,qe,Ge,!1,null,"c53732c8",null)),He=Qe.exports,Xe={name:"UploadModal",props:["config","file","visibilities"],data:function(){return{loading:!1,selectedFolder:"",filename:this.file.name,visibility:this.visibilities.length>0?this.visibilities[0].id:"",overwrite:!1,error:"",existingResourceButton:!1,existingResource:null}},components:{"select-folder":He,modal:le,"v-select":x.a},methods:{handleFolderChange:function(e){this.selectedFolder=e},upload:function(){var e=Object(d["a"])(regeneratorRuntime.mark((function e(){var t,a,i,n,s,o,r=this;return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:for(this.loading=!0,t=new FormData,t.append("file",this.file),t.append("filename",this.filename),t.append("folder",this.selectedFolder),t.append("overwrite",this.overwrite),t.append("visibility",this.visibility),t.append("hide_filename",this.config.hideFilename),a=0,i=Object.entries(this.config.uploadContext);a0&&-1===r.config.allowedTypes.indexOf(e.data.type)?(r.error=r.config.translations.upload_error_unsupported_resource_type+r.config.allowedTypes.join(", "),r.loading=!1):r.$emit("uploaded",e.data)})).catch((function(e){409===e.response.status?(r.error=r.config.translations.upload_error_existing_resource,r.existingResourceButton=!0,r.existingResource=e.response.data,r.loading=!1):(r.error=e.response.data.detail?e.response.data.detail:"Error "+e.response.status+" - "+e.response.statusText,r.loading=!1)}));case 11:case"end":return e.stop()}}),e,this)})));function t(){return e.apply(this,arguments)}return t}()},watch:{visibilities:function(){this.visibility=this.visibilities.length>0?this.visibilities[0].id:""}}},Ze=Xe,et=(a("5af5"),Object(F["a"])(Ze,Ne,Be,!1,null,"2676fd5e",null)),tt=et.exports;function at(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,i)}return a}function it(e){for(var t=1;t0},stringifiedVariations:function(){return JSON.stringify(Me(Ee)(this.selectedImage.variations))}},data:function(){return{mediaModalOpen:!1,cropModalOpen:!1,uploadModalOpen:!1,types:[],folders:[],tags:[],visibilities:[],facetsLoading:!0,newFile:null}},methods:{dispatchVanillaChangeEvent:function(){this.$nextTick((function(){this.$el.dispatchEvent(new CustomEvent("ngrm-change",{detail:{inputFields:this.config.inputFields,selectedImage:this.selectedImage,fieldId:this.fieldId}}))}))},prepareDomForModal:function(){var e=document.querySelector(".ez-page-builder-wrapper");e&&(e.style.transform="none")},resetDomAfterModal:function(){var e=document.querySelector(".ez-page-builder-wrapper");e&&e.removeAttribute("style")},handleMediaModalClose:function(){this.mediaModalOpen=!1,this.resetDomAfterModal(),this.dispatchVanillaChangeEvent()},handleCropModalClose:function(){this.cropModalOpen=!1,this.resetDomAfterModal(),this.dispatchVanillaChangeEvent()},handleUploadModalClose:function(){this.uploadModalOpen=!1,this.dispatchVanillaChangeEvent()},handleMediaSelected:function(e){this.selectedImage={id:e.remoteId,name:e.filename,type:e.type,format:e.format,url:e.url,previewUrl:e.previewUrl,browseUrl:e.browseUrl,alternateText:e.altText,caption:e.caption,watermarkText:this.selectedImage.watermarkText,tags:e.tags,size:e.size,variations:{},height:e.height,width:e.width},this.mediaModalOpen=!1,this.dispatchVanillaChangeEvent()},handleVariationCropChange:function(e){this.selectedImage=it({},this.selectedImage,{variations:it({},this.selectedImage.variations,{},e)}),this.dispatchVanillaChangeEvent()},handleResourceUploaded:function(e){this.selectedImage={id:e.remoteId,name:e.filename,type:e.type,format:e.format,url:e.url,previewUrl:e.previewUrl,browseUrl:e.browseUrl,alternateText:e.altText,caption:e.caption,watermarkText:this.selectedImage.watermarkText,tags:e.tags,size:e.size,variations:{},height:e.height,width:e.width},this.uploadModalOpen=!1,this.dispatchVanillaChangeEvent()},handleCropClicked:function(){this.cropModalOpen=!0,this.prepareDomForModal()},handleRemoveMediaClicked:function(){this.selectedImage={id:"",name:"",type:"image",format:"",url:"",previewUrl:"",browseUrl:"",alternateText:"",caption:"",watermarkText:this.selectedImage.watermarkText,tags:[],size:0,variations:{},height:0,width:0},this.$refs.fileUploadInput.value=null,this.dispatchVanillaChangeEvent()},fetchFacets:function(){var e=Object(d["a"])(regeneratorRuntime.mark((function e(){var t,a,i=this;return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:return e.next=2,fetch(this.config.paths.load_facets);case 2:return t=e.sent,e.next=5,t.json();case 5:a=e.sent,this.types=[],this.tags=[],this.visibilities=[],a.types.forEach((function(e){-1===i.config.allowedTypes.indexOf(e.id)&&0!==i.config.allowedTypes.length||i.types.push(e)})),a.tags.forEach((function(e){-1===i.config.allowedTags.indexOf(e.id)&&0!==i.config.allowedTags.length||i.tags.push(e)})),a.visibilities.forEach((function(e){-1===i.config.allowedVisibilities.indexOf(e.id)&&0!==i.config.allowedVisibilities.length||i.visibilities.push(e)})),this.facetsLoading=!1;case 13:case"end":return e.stop()}}),e,this)})));function t(){return e.apply(this,arguments)}return t}(),handleBrowseMediaClicked:function(){var e=Object(d["a"])(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:this.mediaModalOpen=!0,this.prepareDomForModal(),this.fetchFacets();case 3:case"end":return e.stop()}}),e,this)})));function t(){return e.apply(this,arguments)}return t}(),handleFileInputChange:function(){this.fetchFacets(),this.uploadModalOpen=!0,this.newFile=this.$refs.fileUploadInput.files.item(0)}},watch:{selectedImage:function(){this.$emit("selectedImageChanged",this.selectedImage)}},mounted:function(){this.$nextTick((function(){var e=document.querySelector(".ngrm-model-portal-".concat(this.fieldId));document.body.prepend(e)}))}},st=nt,ot=Object(F["a"])(st,l,c,!1,null,null,null),rt=ot.exports,lt={bind:function(e,t,a){var i=b(t.arg);a.context[i]=t.value}},ct=(a("b39d"),function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",[a("input",{directives:[{name:"model",rawName:"v-model",value:e.selectedFolder,expression:"selectedFolder"}],attrs:{type:"hidden",name:e.config.inputFields.folder},domProps:{value:e.selectedFolder},on:{input:function(t){t.target.composing||(e.selectedFolder=t.target.value)}}}),e.selectedFolder?a("span",[a("i",{staticClass:"fa fa-folder"}),e._v(" "+e._s(this.selectedFolder))]):a("span",[a("i",[e._v(e._s(this.config.translations.select_folder_interaction_empty))])]),a("div",{staticClass:"ngremotemedia-buttons"},[e.selectedFolder?a("input",{attrs:{type:"button",value:"Remove folder"},on:{click:e.handleFolderRemove}}):e._e(),a("input",{attrs:{type:"button",value:this.config.translations.select_folder_interaction_button},on:{click:e.handleSelectFolderModalOpen}})]),e.selectFolderModalOpen?a("modal",{attrs:{title:"Select folder"},on:{close:e.handleSelectFolderModalClose}},[a("select-folder",{attrs:{config:e.config,"selected-folder":e.selectedFolder},on:{select:e.handleFolderSelect}})],1):e._e()],1)}),dt=[],ft={name:"SelectFolderInteraction",props:["config","selectedFolder"],components:{"select-folder":He,modal:le},data:function(){return{selectFolderModalOpen:!1}},methods:{handleSelectFolderModalOpen:function(){this.selectFolderModalOpen=!0},handleSelectFolderModalClose:function(){this.selectFolderModalOpen=!1},handleFolderSelect:function(e){this.selectedFolder=e,this.handleSelectFolderModalClose()},handleFolderRemove:function(){this.selectedFolder=null}}},ut=ft,pt=Object(F["a"])(ut,ct,dt,!1,null,null,null),ht=pt.exports;i["default"].config.productionTip=!1,i["default"].use(s.a),i["default"].use(r.a);var mt=function(e){window["ngrm_interactions_vue_".concat(e.dataset.id)]=new i["default"]({el:e,directives:{init:lt},data:{config:{paths:{browse_resources:"/resource/browse",upload_resources:"/resource/upload",load_facets:"/facets/load",load_folders:"/folder/load",create_folder:"/folder/create"},translations:{},inputFields:{locationId:"locationId",remoteId:"remoteId",type:"type",altText:"altText",caption:"caption",tags:"tags[]",cropSettings:"cropSettings",source:"source",watermarkText:"watermarkText"},availableVariations:[],allowedVisibilities:[],allowedTypes:[],allowedTags:[],parentFolder:null,folder:null,uploadContext:{},disableUpload:!1,hideFilename:!1},selectedImage:{id:"",name:"",type:"image",format:"",url:"",browse_url:"",previewUrl:"",alternateText:"",caption:"",watermarkText:"",tags:[],size:"",variations:{},height:0,width:0}},components:{interactions:rt}})},gt=function(e){window["ngrm_select_folder_vue_".concat(e.dataset.id)]=new i["default"]({el:e,directives:{init:lt},data:{config:{paths:{load_folders:"/folder/load",create_folder:"/folder/create"},translations:{},inputFields:{folder:"folder"}},selectedFolder:null},components:{"select-folder-interaction":ht}})},vt=function(){for(var e=document.getElementsByClassName("ngremotemedia-container"),t=0;t
- + + diff --git a/lib/Core/Provider/Cloudinary/Gateway/CloudinaryApiGateway.php b/lib/Core/Provider/Cloudinary/Gateway/CloudinaryApiGateway.php index c22c78c0..5c63ef47 100644 --- a/lib/Core/Provider/Cloudinary/Gateway/CloudinaryApiGateway.php +++ b/lib/Core/Provider/Cloudinary/Gateway/CloudinaryApiGateway.php @@ -34,6 +34,7 @@ use function count; use function date; use function floor; +use function is_array; use function log; use function max; use function min; @@ -311,6 +312,12 @@ public function getVideoThumbnail(CloudinaryRemoteId $remoteId, array $options = ); } + if (!is_array($options['transformation'] ?? null)) { + $options['transformation'] = []; + } + + $options['transformation']['fetch_format'] = 'jpg'; + return (string) Image::fromParams($remoteId->getResourceId(), $options)->toUrl(); } diff --git a/tests/bundle/Controller/Resource/BrowseTest.php b/tests/bundle/Controller/Resource/BrowseTest.php index 1933eaa1..c04ba359 100644 --- a/tests/bundle/Controller/Resource/BrowseTest.php +++ b/tests/bundle/Controller/Resource/BrowseTest.php @@ -109,36 +109,36 @@ public function test(): void $image1BrowseVariation = new RemoteResourceVariation( $result->getResources()[0], - 'https://cloudinary.com/test/c_fit_160_120/upload/images/image.jpg', + 'https://cloudinary.com/test/c_fit_160_120/f_jpg/upload/images/image.jpg', ); $image1PreviewVariation = new RemoteResourceVariation( $result->getResources()[0], - 'https://cloudinary.com/test/c_fit_800_600/upload/images/image.jpg', + 'https://cloudinary.com/test/c_fit_800_600/f_jpg/upload/images/image.jpg', ); $image2BrowseVariation = new RemoteResourceVariation( $result->getResources()[1], - 'https://cloudinary.com/test/c_fit_160_120/upload/images/image2.jpg', + 'https://cloudinary.com/test/c_fit_160_120/f_jpg/upload/images/image2.jpg', ); $image2PreviewVariation = new RemoteResourceVariation( $result->getResources()[1], - 'https://cloudinary.com/test/c_fit_800_600/upload/images/image2.jpg', + 'https://cloudinary.com/test/c_fit_800_600/f_jpg/upload/images/image2.jpg', ); $videoThumbnailBrowseVariation = new RemoteResourceVariation( $result->getResources()[2], - 'https://cloudinary.com/test/c_fit_160_120/upload/videos/example.mp4.jpg', + 'https://cloudinary.com/test/c_fit_160_120/f_jpg/upload/videos/example.mp4.jpg', ); - $videoThumbnailPreviewVariation = new RemoteResourceVariation( + $videoPreviewVariation = new RemoteResourceVariation( $result->getResources()[2], 'https://cloudinary.com/test/c_fit_800_600/upload/videos/example.mp4.jpg', ); $this->providerMock - ->expects(self::exactly(4)) + ->expects(self::exactly(5)) ->method('buildVariation') ->willReturnCallback( static fn ( @@ -146,25 +146,18 @@ public function test(): void string $variationGroup, string $variationName ) => match ($location->getRemoteResource()->getRemoteId()) { - 'upload|image|media/images/image.jpg' => $variationName === 'browse' ? $image1BrowseVariation : $image1PreviewVariation, - 'upload|image|media/images/image2.jpg' => $variationName === 'browse' ? $image2BrowseVariation : $image2PreviewVariation, + 'upload|image|media/images/image.jpg' => $variationName === 'browse_image' ? $image1BrowseVariation : $image1PreviewVariation, + 'upload|image|media/images/image2.jpg' => $variationName === 'browse_image' ? $image2BrowseVariation : $image2PreviewVariation, + 'upload|image|media/videos/example.mp4' => $videoPreviewVariation, default => null, }, ); $this->providerMock - ->expects(self::exactly(2)) + ->expects(self::once()) ->method('buildVideoThumbnailVariation') - ->willReturnCallback( - static fn ( - RemoteResourceLocation $location, - string $variationGroup, - string $variationName - ) => match ($location->getRemoteResource()->getRemoteId()) { - 'upload|image|media/videos/example.mp4' => $variationName === 'browse' ? $videoThumbnailBrowseVariation : $videoThumbnailPreviewVariation, - default => null, - }, - ); + ->with(new RemoteResourceLocation($result->getResources()[2]), 'ngrm_interface', 'browse') + ->willReturn($videoThumbnailBrowseVariation); $expectedResponseContent = json_encode([ 'hits' => [ @@ -180,8 +173,8 @@ public function test(): void 'filename' => 'image.jpg', 'originalFilename' => null, 'format' => null, - 'browseUrl' => 'https://cloudinary.com/test/c_fit_160_120/upload/images/image.jpg', - 'previewUrl' => 'https://cloudinary.com/test/c_fit_800_600/upload/images/image.jpg', + 'browseUrl' => 'https://cloudinary.com/test/c_fit_160_120/f_jpg/upload/images/image.jpg', + 'previewUrl' => 'https://cloudinary.com/test/c_fit_800_600/f_jpg/upload/images/image.jpg', 'url' => 'https://cloudinary.com/test/upload/images/image.jpg', 'altText' => null, 'caption' => null, @@ -198,8 +191,8 @@ public function test(): void 'filename' => 'image2.jpg', 'originalFilename' => 'orig_image2.jpg', 'format' => null, - 'browseUrl' => 'https://cloudinary.com/test/c_fit_160_120/upload/images/image2.jpg', - 'previewUrl' => 'https://cloudinary.com/test/c_fit_800_600/upload/images/image2.jpg', + 'browseUrl' => 'https://cloudinary.com/test/c_fit_160_120/f_jpg/upload/images/image2.jpg', + 'previewUrl' => 'https://cloudinary.com/test/c_fit_800_600/f_jpg/upload/images/image2.jpg', 'url' => 'https://cloudinary.com/test/upload/images/image2.jpg', 'altText' => 'test alt text', 'caption' => 'test caption', @@ -216,7 +209,7 @@ public function test(): void 'filename' => 'example.mp4', 'originalFilename' => 'example.mp4', 'format' => null, - 'browseUrl' => 'https://cloudinary.com/test/c_fit_160_120/upload/videos/example.mp4.jpg', + 'browseUrl' => 'https://cloudinary.com/test/c_fit_160_120/f_jpg/upload/videos/example.mp4.jpg', 'previewUrl' => 'https://cloudinary.com/test/c_fit_800_600/upload/videos/example.mp4.jpg', 'url' => 'https://cloudinary.com/test/upload/videos/example.mp4', 'altText' => 'some alt text', diff --git a/tests/bundle/Controller/Resource/UploadTest.php b/tests/bundle/Controller/Resource/UploadTest.php index 1f62ff9c..50a2ed59 100644 --- a/tests/bundle/Controller/Resource/UploadTest.php +++ b/tests/bundle/Controller/Resource/UploadTest.php @@ -137,7 +137,7 @@ public function testUpload(): void string $variationGroup, string $variationName ) => match ($location->getRemoteResource()->getRemoteId()) { - 'upload|image|media/image/sample_image.jpg' => $variationName === 'browse' ? $browseVariation : $previewVariation, + 'upload|image|media/image/sample_image.jpg' => $variationName === 'browse_image' ? $browseVariation : $previewVariation, default => null, }, ); @@ -290,7 +290,7 @@ public function testUploadProtectedWithContext(): void string $variationGroup, string $variationName ) => match ($location->getRemoteResource()->getRemoteId()) { - 'authenticated|image|media/image/sample_image.jpg' => $variationName === 'browse_protected' ? $browseVariation : $previewVariation, + 'authenticated|image|media/image/sample_image.jpg' => $variationName === 'browse_protected_image' ? $browseVariation : $previewVariation, default => null, }, ); @@ -388,7 +388,7 @@ public function testUploadExistingFile(): void $uploadedFileMock ->expects(self::exactly(3)) ->method('getRealPath') - ->willReturn('/var/www/project/sample_image.jpg'); + ->willReturn('/var/www/project/sample_video.mp4'); $uploadedFileMock ->expects(self::exactly(2)) @@ -407,7 +407,7 @@ public function testUploadExistingFile(): void $this->fileHashFactoryMock ->expects(self::once()) ->method('createHash') - ->with('/var/www/project/sample_image.jpg') + ->with('/var/www/project/sample_video.mp4') ->willReturn('a522f23sf81aa0afd03387c37e2b6eax'); $fileStruct = FileStruct::fromUploadedFile($uploadedFileMock); @@ -421,13 +421,13 @@ public function testUploadExistingFile(): void ); $resource = new RemoteResource( - remoteId: 'upload|image|sample_image.jpg', - type: 'image', - url: 'https://cloudinary.com/test/upload/image/sample_image.jpg', + remoteId: 'upload|video|sample_video.mp4', + type: 'video', + url: 'https://cloudinary.com/test/upload/video/sample_video.mp4', md5: 'a522f23sf81aa0afd03387c37e2b6eax', - name: 'sample_image.jpg', + name: 'sample_video.mp4', folder: null, - size: 123, + size: 12345, ); $this->providerMock @@ -438,43 +438,41 @@ public function testUploadExistingFile(): void $browseVariation = new RemoteResourceVariation( $resource, - 'https://cloudinary.com/test/c_fit_160_120/upload/image/sample_image.jpg', + 'https://cloudinary.com/test/c_fit_160_120/f_jpg/upload/video/sample_video.mp4', ); $previewVariation = new RemoteResourceVariation( $resource, - 'https://cloudinary.com/test/c_fit_800_600/upload/image/sample_image.jpg', + 'https://cloudinary.com/test/c_fit_800_600/upload/video/sample_video.mp4', ); $this->providerMock - ->expects(self::exactly(2)) + ->expects(self::once()) ->method('buildVariation') - ->willReturnCallback( - static fn ( - RemoteResourceLocation $location, - string $variationGroup, - string $variationName - ) => match ($location->getRemoteResource()->getRemoteId()) { - 'upload|image|sample_image.jpg' => $variationName === 'browse' ? $browseVariation : $previewVariation, - default => null, - }, - ); + ->with(new RemoteResourceLocation($resource), 'ngrm_interface', 'preview') + ->willReturn($previewVariation); + + $this->providerMock + ->expects(self::once()) + ->method('buildVideoThumbnailVariation') + ->with(new RemoteResourceLocation($resource), 'ngrm_interface', 'browse') + ->willReturn($browseVariation); $expectedResponseContent = json_encode([ - 'remoteId' => 'upload|image|sample_image.jpg', + 'remoteId' => 'upload|video|sample_video.mp4', 'folder' => null, 'tags' => [], - 'type' => 'image', + 'type' => 'video', 'visibility' => 'public', - 'size' => 123, + 'size' => 12345, 'width' => null, 'height' => null, - 'filename' => 'sample_image.jpg', + 'filename' => 'sample_video.mp4', 'originalFilename' => null, 'format' => null, - 'browseUrl' => 'https://cloudinary.com/test/c_fit_160_120/upload/image/sample_image.jpg', - 'previewUrl' => 'https://cloudinary.com/test/c_fit_800_600/upload/image/sample_image.jpg', - 'url' => 'https://cloudinary.com/test/upload/image/sample_image.jpg', + 'browseUrl' => 'https://cloudinary.com/test/c_fit_160_120/f_jpg/upload/video/sample_video.mp4', + 'previewUrl' => 'https://cloudinary.com/test/c_fit_800_600/upload/video/sample_video.mp4', + 'url' => 'https://cloudinary.com/test/upload/video/sample_video.mp4', 'altText' => null, 'caption' => null, ]); @@ -616,7 +614,7 @@ public function testUploadExistingFileName(): void string $variationGroup, string $variationName ) => match ($location->getRemoteResource()->getRemoteId()) { - 'upload|image|media/image/sample_image.jpg' => $variationName === 'browse' ? $browseVariation : $previewVariation, + 'upload|image|media/image/sample_image.jpg' => $variationName === 'browse_image' ? $browseVariation : $previewVariation, default => null, }, ); diff --git a/tests/lib/Core/Provider/Cloudinary/Gateway/CloudinaryApiGatewayTest.php b/tests/lib/Core/Provider/Cloudinary/Gateway/CloudinaryApiGatewayTest.php index 085e0195..23c2105d 100644 --- a/tests/lib/Core/Provider/Cloudinary/Gateway/CloudinaryApiGatewayTest.php +++ b/tests/lib/Core/Provider/Cloudinary/Gateway/CloudinaryApiGatewayTest.php @@ -827,7 +827,7 @@ public function testGetVideoThumbnail(): void $cloudinaryRemoteId = CloudinaryRemoteId::fromRemoteId('upload|video|media/example'); self::assertSame( - 'https://res.cloudinary.com/testcloud/video/upload/media/example', + 'https://res.cloudinary.com/testcloud/video/upload/f_jpg/media/example', $this->apiGateway->getVideoThumbnail($cloudinaryRemoteId), ); } @@ -837,9 +837,15 @@ public function testGetVideoThumbnailAuthenticated(): void $cloudinaryRemoteId = CloudinaryRemoteId::fromRemoteId('upload|video|media/example'); $token = AuthToken::fromExpiresAt(new DateTimeImmutable('2023/1/1')); + $options = [ + 'transformation' => [[ + 'fetch_format' => 'png', + ]], + ]; + self::assertSame( - 'https://res.cloudinary.com/testcloud/video/upload/media/example?__cld_token__=exp=1672531200~hmac=91194b19360a54349173e96f49135838cdabd3cdb07d97eb2f12b60d8168e5cc', - $this->apiGateway->getVideoThumbnail($cloudinaryRemoteId, [], $token), + 'https://res.cloudinary.com/testcloud/video/upload/f_jpg/media/example?__cld_token__=exp=1672531200~hmac=566ec5046c26254b12b2dc36c84c1392034a9a5e627af0ef9abc853464b4a6ef', + $this->apiGateway->getVideoThumbnail($cloudinaryRemoteId, $options, $token), ); } From 25c5acf394f24159d31ed9c9d8e10858f5c148fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20=C4=8Cupi=C4=87?= Date: Thu, 28 Nov 2024 11:51:21 +0100 Subject: [PATCH 13/13] Run CS fixer --- bundle/Command/RefreshStoredResourcesCommand.php | 8 ++++---- bundle/Controller/Resource/AbstractController.php | 2 +- bundle/Templating/Twig/Extension/RemoteMediaExtension.php | 4 ++-- lib/API/Search/Query.php | 4 +++- lib/API/Search/Result.php | 6 ++++-- lib/API/Values/RemoteResource.php | 4 ++-- lib/API/Values/RemoteResourceLocation.php | 4 ++-- lib/Core/AbstractProvider.php | 4 ++-- lib/Core/Transformation/Registry.php | 2 +- lib/Form/DataTransformer/RemoteMediaTransformer.php | 2 +- 10 files changed, 22 insertions(+), 18 deletions(-) diff --git a/bundle/Command/RefreshStoredResourcesCommand.php b/bundle/Command/RefreshStoredResourcesCommand.php index 31e12737..90e73bcb 100644 --- a/bundle/Command/RefreshStoredResourcesCommand.php +++ b/bundle/Command/RefreshStoredResourcesCommand.php @@ -29,7 +29,7 @@ final class RefreshStoredResourcesCommand extends Command private int $batchSize = 500; - /** @var \Netgen\RemoteMedia\API\Values\RemoteResource[] */ + /** @var RemoteResource[] */ private array $resourcesToDelete; public function __construct( @@ -97,7 +97,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } /** - * @return \Netgen\RemoteMedia\API\Values\RemoteResource[] + * @return RemoteResource[] */ private function getBatch(int $limit, int $offset): array { @@ -105,7 +105,7 @@ private function getBatch(int $limit, int $offset): array } /** - * @param \Netgen\RemoteMedia\API\Values\RemoteResource[] $resources + * @param RemoteResource[] $resources */ private function refreshResources(array $resources): void { @@ -135,7 +135,7 @@ private function refreshResources(array $resources): void /** * @param string[] $remoteIds * - * @return array + * @return array */ private function getRemoteBatch(array $remoteIds): array { diff --git a/bundle/Controller/Resource/AbstractController.php b/bundle/Controller/Resource/AbstractController.php index e2d0ad62..5cad638b 100644 --- a/bundle/Controller/Resource/AbstractController.php +++ b/bundle/Controller/Resource/AbstractController.php @@ -52,7 +52,7 @@ protected function formatResource(RemoteResource $resource): array } /** - * @param \Netgen\RemoteMedia\API\Values\RemoteResource[] $resources + * @param RemoteResource[] $resources */ protected function formatResources(array $resources): array { diff --git a/bundle/Templating/Twig/Extension/RemoteMediaExtension.php b/bundle/Templating/Twig/Extension/RemoteMediaExtension.php index c19ce670..b09b6005 100644 --- a/bundle/Templating/Twig/Extension/RemoteMediaExtension.php +++ b/bundle/Templating/Twig/Extension/RemoteMediaExtension.php @@ -12,7 +12,7 @@ final class RemoteMediaExtension extends AbstractExtension { /** - * @return \Twig\TwigFunction[] + * @return TwigFunction[] */ public function getFunctions(): array { @@ -97,7 +97,7 @@ public function getFunctions(): array } /** - * @return \Twig\TwigFilter[] + * @return TwigFilter[] */ public function getFilters(): array { diff --git a/lib/API/Search/Query.php b/lib/API/Search/Query.php index 01fe7ae2..0c564a1c 100644 --- a/lib/API/Search/Query.php +++ b/lib/API/Search/Query.php @@ -4,6 +4,8 @@ namespace Netgen\RemoteMedia\API\Search; +use Netgen\RemoteMedia\API\Values\Folder; + use function get_object_vars; use function http_build_query; use function implode; @@ -13,7 +15,7 @@ final class Query { /** * @param string[] $types - * @param \Netgen\RemoteMedia\API\Values\Folder[] $folders + * @param Folder[] $folders * @param string[] $visibilities * @param string[] $tags * @param string[] $remoteIds diff --git a/lib/API/Search/Result.php b/lib/API/Search/Result.php index b2e4d9ae..a3a6993c 100644 --- a/lib/API/Search/Result.php +++ b/lib/API/Search/Result.php @@ -4,10 +4,12 @@ namespace Netgen\RemoteMedia\API\Search; +use Netgen\RemoteMedia\API\Values\RemoteResource; + final class Result { /** - * @param \Netgen\RemoteMedia\API\Values\RemoteResource[] $resources + * @param RemoteResource[] $resources */ public function __construct( private int $totalCount, @@ -26,7 +28,7 @@ public function getNextCursor(): ?string } /** - * @return \Netgen\RemoteMedia\API\Values\RemoteResource[] + * @return RemoteResource[] */ public function getResources(): array { diff --git a/lib/API/Values/RemoteResource.php b/lib/API/Values/RemoteResource.php index 7df6f4fe..c122ea04 100644 --- a/lib/API/Values/RemoteResource.php +++ b/lib/API/Values/RemoteResource.php @@ -42,7 +42,7 @@ class RemoteResource * @param string[] $tags * @param array $metadata * @param array $context - * @param Collection|\Netgen\RemoteMedia\API\Values\RemoteResourceLocation[] $locations + * @param Collection|RemoteResourceLocation[] $locations */ public function __construct( private string $remoteId, @@ -343,7 +343,7 @@ public function removeContextProperty(string $name): self } /** - * @return Collection|\Netgen\RemoteMedia\API\Values\RemoteResourceLocation[] + * @return Collection|RemoteResourceLocation[] */ public function getLocations(): array|Collection { diff --git a/lib/API/Values/RemoteResourceLocation.php b/lib/API/Values/RemoteResourceLocation.php index b7fd8e07..201068c1 100644 --- a/lib/API/Values/RemoteResourceLocation.php +++ b/lib/API/Values/RemoteResourceLocation.php @@ -41,7 +41,7 @@ public function setSource(?string $source): self } /** - * @return \Netgen\RemoteMedia\API\Values\CropSettings[] + * @return CropSettings[] */ public function getCropSettings(): array { @@ -49,7 +49,7 @@ public function getCropSettings(): array } /** - * @param \Netgen\RemoteMedia\API\Values\CropSettings[] $cropSettings + * @param CropSettings[] $cropSettings */ public function setCropSettings(array $cropSettings): self { diff --git a/lib/Core/AbstractProvider.php b/lib/Core/AbstractProvider.php index 349f84b6..11a05a52 100644 --- a/lib/Core/AbstractProvider.php +++ b/lib/Core/AbstractProvider.php @@ -46,7 +46,7 @@ public function __construct( } /** - * @return \Netgen\RemoteMedia\API\Values\Folder[] + * @return Folder[] * * @throws NotSupportedException */ @@ -356,7 +356,7 @@ public function generateRawVariationHtmlTag( } /** - * @return \Netgen\RemoteMedia\API\Values\Folder[] + * @return Folder[] */ abstract protected function internalListFolders(?Folder $parent = null): array; diff --git a/lib/Core/Transformation/Registry.php b/lib/Core/Transformation/Registry.php index 350f0a88..8e57cf99 100644 --- a/lib/Core/Transformation/Registry.php +++ b/lib/Core/Transformation/Registry.php @@ -8,7 +8,7 @@ final class Registry { - /** @var \Netgen\RemoteMedia\Core\Transformation\HandlerInterface[] */ + /** @var HandlerInterface[] */ private array $transformationHandlers = []; public function addHandler(string $provider, string $identifier, HandlerInterface $transformationHandler): void diff --git a/lib/Form/DataTransformer/RemoteMediaTransformer.php b/lib/Form/DataTransformer/RemoteMediaTransformer.php index edc6dd73..112706ae 100644 --- a/lib/Form/DataTransformer/RemoteMediaTransformer.php +++ b/lib/Form/DataTransformer/RemoteMediaTransformer.php @@ -110,7 +110,7 @@ private function resolveCropSettingsString(RemoteResourceLocation $location): st } /** - * @return \Netgen\RemoteMedia\API\Values\CropSettings[] + * @return CropSettings[] */ private function resolveCropSettings(array $data): array {