From 966a60511905715a5620f90f0d002d8bd40f7e17 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/12] 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 e5f2a7c73ad8f82cd3c3b96afd05ec9537b1301c 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/12] 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 1440a70994c45dd2c4075b4840c9b4706d9f49f6 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/12] 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 711a0b857083975d93b85f1bf26f2ba60ece2a82 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/12] 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 c111bd6c..c2afe6fb 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: @@ -79,6 +81,7 @@ services: - '@netgen_remote_media.provider.cloudinary.converter.resource_type' - '@netgen_remote_media.provider.cloudinary.converter.visibility_type' - '@netgen_remote_media.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 @@ -97,6 +100,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 @@ -104,6 +108,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 71f2a7b30e93bc58cf1087898697a433dd9acb66 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/12] 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 5fd5c669d9892fd6526a70fc4d59d8d2d431e215 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/12] 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 b55a167b9d9877b9bded6e4ee74cf351ecb88e2b 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/12] 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 805430d8709121a3fb56c6d61c42d2940c2febf9 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/12] 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 0fcdb930a035152b9cde5dc0416a546b82909fa8 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/12] 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 993dc1767dd04cfa680a820673e327b7e0be8200 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/12] 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 477215dfe6830fff65d16557b57cee1eb25ab938 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/12] 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 cd2d1fa65c894c21103b1d8f43fdbd4f537d9729 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/12] 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), ); }