Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
0822f30
SPLAT-1442 add POC for ckeditor plugin
amalija-ramljak Mar 26, 2024
38ffbab
SPLAT-1442 [wip] add poc for rendering proper structure for vue
amalija-ramljak Mar 28, 2024
abff848
SPLAT-1442 enable plugin config, fix data casting flow to enable savi…
amalija-ramljak Apr 3, 2024
134e8e3
SPLAT-1442 fix json stringify for data param (encoding)
amalija-ramljak Apr 3, 2024
dece186
SPLAT-1442 fix error when upload is disabled
amalija-ramljak Apr 4, 2024
bf6055d
SPLAT-1442 fix attributes and rerendering, handle image alignment
amalija-ramljak Apr 4, 2024
f50ae53
SPLAT-1442 vue app prod build
amalija-ramljak Apr 4, 2024
b936752
SPLAT-1442 change casting flow to render properly on FE
amalija-ramljak Apr 4, 2024
5d3a134
SPLAT-1442 fix operation handling, stub to handle removal
amalija-ramljak Apr 4, 2024
df36e49
SPLAT-1442 define insert/delete endpoint config, define location id a…
amalija-ramljak Apr 4, 2024
7f52a37
SPLAT-1442 implement util to transform html to ckeditor view elements
amalija-ramljak Apr 10, 2024
501776c
SPLAT-1442 [wip] handle customizable template, add css class and vari…
amalija-ramljak Apr 11, 2024
227345c
SPLAT-1442 fix and finish editor to view flow
amalija-ramljak Apr 18, 2024
72560af
SPLAT-1442 fix alignment style
amalija-ramljak Apr 26, 2024
f991743
SPLAT-1442 handle deleting from editor
amalija-ramljak Apr 26, 2024
b2d42e8
SPLAT-1442 fix quotation marks
amalija-ramljak Apr 26, 2024
08d4998
SPLAT-1442 fallback endpoints to default
amalija-ramljak Apr 26, 2024
8fa1a03
SPLAT-1442 fix field id uniqueness
amalija-ramljak May 17, 2024
6028bf7
SPLAT-1442 fix resource template
amalija-ramljak May 17, 2024
ce63d0a
SPLAT-1442 add call to render ngrm views
amalija-ramljak May 17, 2024
fab1ac6
SPLAT-1442 missing export keyword
amalija-ramljak Jun 18, 2024
304d3b7
SPLAT-1442 fix service definition and configuration
amalija-ramljak Jul 15, 2024
5ef9d97
SPLAT-1442 fix upload context and allow upload by default
amalija-ramljak Jul 15, 2024
0ea3b09
SPLAT-1442 change text
amalija-ramljak Jul 15, 2024
832f21b
SPLAT-1442 fix argument
amalija-ramljak Jul 15, 2024
4511bd8
SPLAT-1442 show all variations to pick from (not just crops)
amalija-ramljak Jul 16, 2024
0e2e7ae
SPLAT-1442 replace placeholder element instead of injecting children
amalija-ramljak Sep 16, 2024
1bbeaf9
SPLAT-1442 fix scroll into view call
amalija-ramljak Sep 16, 2024
7238eba
SPLAT-1442 separate reading upload context from query
amalija-ramljak Sep 16, 2024
407e6a6
SPLAT-1442 dont delete location on drag and drop
amalija-ramljak Sep 17, 2024
edec685
SPLAT-1442 add image source for location
amalija-ramljak Sep 17, 2024
836c430
Implement unit tests for Location controllers
igorkrz Jan 24, 2025
0b30a14
SPLAT-1442 remove extra semicolon
amalija-ramljak Feb 6, 2025
f88ce41
SPLAT-1442 rebuild for production
amalija-ramljak Feb 6, 2025
3f81d28
SPLAT-1442 run cs fixer
amalija-ramljak Feb 6, 2025
1767875
SPLAT-1442 remove extension of AbstractController
amalija-ramljak Feb 7, 2025
79bde53
Fix failing unit tests for selected image
igorkrz Feb 7, 2025
d3a7374
Fix RemoteMediaTransformerTest
igorkrz Feb 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions bundle/Controller/Configuration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<?php

declare(strict_types=1);

namespace Netgen\Bundle\RemoteMediaBundle\Controller;

use Netgen\RemoteMedia\Core\Resolver\Variation as VariationResolver;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

use function array_keys;

final class Configuration
{
public function __construct(
private readonly RouterInterface $router,
private readonly TranslatorInterface $translator,
private readonly VariationResolver $variationResolver,
) {}

public function __invoke(Request $request): JsonResponse
{
return new JsonResponse([
'paths' => $this->resolvePaths(),
'translations' => $this->resolveTranslations(),
'availableVariations' => $this->resolveAvailableVariations($request),
'allVariations' => $this->resolveAllVariations($request),
'uploadContext' => $this->resolveUploadContext($request),
]);
}

private function resolvePaths(): array
{
return [
'browse_resources' => $this->router->generate('netgen_remote_media_ajax_resource_browse'),
'upload_resources' => $this->router->generate('netgen_remote_media_ajax_resource_upload'),
'load_facets' => $this->router->generate('netgen_remote_media_ajax_facets_load'),
'load_folders' => $this->router->generate('netgen_remote_media_ajax_folder_load'),
'create_folder' => $this->router->generate('netgen_remote_media_ajax_folder_create'),
];
}

private function resolveTranslations(): array
{
return [
'browse_title' => $this->translator->trans('ngrm.edit.vue.browse.title', [], 'ngremotemedia'),
'browse_select_type' => $this->translator->trans('ngrm.edit.vue.browse.facets.select_type', [], 'ngremotemedia'),
'browse_loading_types' => $this->translator->trans('ngrm.edit.vue.browse.facets.loading_types', [], 'ngremotemedia'),
'browse_all_types' => $this->translator->trans('ngrm.edit.vue.browse.facets.all_types', [], 'ngremotemedia'),
'browse_select_folder' => $this->translator->trans('ngrm.edit.vue.browse.facets.select_folder', [], 'ngremotemedia'),
'browse_loading_folders' => $this->translator->trans('ngrm.edit.vue.browse.facets.loading_folders', [], 'ngremotemedia'),
'browse_all_folders' => $this->translator->trans('ngrm.edit.vue.browse.facets.all_folders', [], 'ngremotemedia'),
'browse_select_tag' => $this->translator->trans('ngrm.edit.vue.browse.facets.select_tag', [], 'ngremotemedia'),
'browse_loading_tags' => $this->translator->trans('ngrm.edit.vue.browse.facets.loading_tags', [], 'ngremotemedia'),
'browse_all_tags' => $this->translator->trans('ngrm.edit.vue.browse.facets.all_tags', [], 'ngremotemedia'),
'browse_select_visibility' => $this->translator->trans('ngrm.edit.vue.browse.facets.select_visibility', [], 'ngremotemedia'),
'browse_loading_visibilities' => $this->translator->trans('ngrm.edit.vue.browse.facets.loading_visibilities', [], 'ngremotemedia'),
'browse_all_visibilities' => $this->translator->trans('ngrm.edit.vue.browse.facets.all_visibilities', [], 'ngremotemedia'),
'search' => $this->translator->trans('ngrm.edit.vue.browse.facets.search', [], 'ngremotemedia'),
'search_placeholder' => $this->translator->trans('ngrm.edit.vue.browse.facets.search_placeholder', [], 'ngremotemedia'),
'browse_empty_folder' => $this->translator->trans('ngrm.edit.vue.browse.empty_folder', [], 'ngremotemedia'),
'browse_empty_folder_hint' => $this->translator->trans('ngrm.edit.vue.browse.empty_folder_hint', [], 'ngremotemedia'),
'browse_select' => $this->translator->trans('ngrm.edit.vue.browse.select', [], 'ngremotemedia'),
'load_more' => $this->translator->trans('ngrm.edit.vue.browse.load_more', [], 'ngremotemedia'),
'crop_modal_title' => $this->translator->trans('ngrm.edit.vue.crop.modal_title', [], 'ngremotemedia'),
'crop_reset' => $this->translator->trans('ngrm.edit.vue.crop.reset', [], 'ngremotemedia'),
'crop_apply' => $this->translator->trans('ngrm.edit.vue.crop.apply', [], 'ngremotemedia'),
'crop_cancel' => $this->translator->trans('ngrm.edit.vue.crop.cancel', [], 'ngremotemedia'),
'crop_add' => $this->translator->trans('ngrm.edit.vue.crop.add', [], 'ngremotemedia'),
'crop_preview' => $this->translator->trans('ngrm.edit.vue.crop.preview', [], 'ngremotemedia'),
'crop_save_sizes' => $this->translator->trans('ngrm.edit.vue.crop.save_sizes', [], 'ngremotemedia'),
'crop_add_size' => $this->translator->trans('ngrm.edit.vue.crop.add_size', [], 'ngremotemedia'),
'crop_added_for_confirmation' => $this->translator->trans('ngrm.edit.vue.crop.added_for_confirmation', [], 'ngremotemedia'),
'crop_media_too_small' => $this->translator->trans('ngrm.edit.vue.crop.media_too_small', [], 'ngremotemedia'),
'media_gallery_empty_folder' => $this->translator->trans('ngrm.edit.vue.media_gallery.empty_folder', [], 'ngremotemedia'),
'media_gallery_upload_media' => $this->translator->trans('ngrm.edit.vue.media_gallery.upload_media', [], 'ngremotemedia'),
'media_gallery_select' => $this->translator->trans('ngrm.edit.vue.media_gallery.select', [], 'ngremotemedia'),
'media_gallery_load_more' => $this->translator->trans('ngrm.edit.vue.media_gallery.load_more', [], 'ngremotemedia'),
'Search for media' => $this->translator->trans('Search for media', [], 'ngremotemedia'),
'Load more' => $this->translator->trans('Load more', [], 'ngremotemedia'),
'Upload new media' => $this->translator->trans('Upload new media', [], 'ngremotemedia'),
'No results' => $this->translator->trans('No results', [], 'ngremotemedia'),
'Alternate text' => $this->translator->trans('Alternate text', [], 'ngremotemedia'),
'Class' => $this->translator->trans('CSS class', [], 'ngremotemedia'),
'Create new folder?' => $this->translator->trans('Create new folder?', [], 'ngremotemedia'),
'Folder' => $this->translator->trans('Folder', [], 'ngremotemedia'),
'All' => $this->translator->trans('All', [], 'ngremotemedia'),
'Add tag' => $this->translator->trans('Add tag', [], 'ngremotemedia'),
'Media type' => $this->translator->trans('Media type', [], 'ngremotemedia'),
'Image' => $this->translator->trans('Image and documents', [], 'ngremotemedia'),
'Video' => $this->translator->trans('Video', [], 'ngremotemedia'),
'Loading...' => $this->translator->trans('Loading...', [], 'ngremotemedia'),
'Cancel' => $this->translator->trans('Cancel', [], 'ngremotemedia'),
'Save all' => $this->translator->trans('Save all', [], 'ngremotemedia'),
'Generate' => $this->translator->trans('Generate', [], 'ngremotemedia'),
'Caption' => $this->translator->trans('Caption', [], 'ngremotemedia'),
'by' => $this->translator->trans('by', [], 'ngremotemedia'),
'name' => $this->translator->trans('name', [], 'ngremotemedia'),
'tag' => $this->translator->trans('tag', [], 'ngremotemedia'),
'Image is to small for this version' => $this->translator->trans('Image is to small for this version', [], 'ngremotemedia'),
'close' => $this->translator->trans('Close', [], 'ngremotemedia'),
'next' => $this->translator->trans('Next 25 &gt;', [], 'ngremotemedia'),
'prev' => $this->translator->trans('&lt; Previous 25', [], 'ngremotemedia'),
'interactions_scale' => $this->translator->trans('ngrm.edit.vue.interactions.scale', [], 'ngremotemedia'),
'interactions_no_media_selected' => $this->translator->trans('ngrm.edit.vue.interactions.no_media_selected', [], 'ngremotemedia'),
'interactions_remove_media' => $this->translator->trans('ngrm.edit.vue.interactions.remove_media', [], 'ngremotemedia'),
'interactions_select_media' => $this->translator->trans('ngrm.edit.vue.interactions.select_media', [], 'ngremotemedia'),
'interactions_manage_media' => $this->translator->trans('ngrm.edit.vue.interactions.manage_media', [], 'ngremotemedia'),
'interactions_quick_upload' => $this->translator->trans('ngrm.edit.vue.interactions.quick_upload', [], 'ngremotemedia'),
'preview_alternate_text' => $this->translator->trans('ngrm.edit.vue.preview.alternate_text', [], 'ngremotemedia'),
'preview_alternate_text_info' => $this->translator->trans('ngrm.edit.vue.preview.alternate_text_info', [], 'ngremotemedia'),
'preview_caption' => $this->translator->trans('ngrm.edit.vue.preview.caption', [], 'ngremotemedia'),
'preview_caption_info' => $this->translator->trans('ngrm.edit.vue.preview.caption_info', [], 'ngremotemedia'),
'preview_tags' => $this->translator->trans('ngrm.edit.vue.preview.tags', [], 'ngremotemedia'),
'preview_tags_info' => $this->translator->trans('ngrm.edit.vue.preview.tags_info', [], 'ngremotemedia'),
'preview_watermark_text' => $this->translator->trans('ngrm.edit.vue.preview.watermark_text', [], 'ngremotemedia'),
'preview_watermark_text_info' => $this->translator->trans('ngrm.edit.vue.preview.watermark_text_info', [], 'ngremotemedia'),
'preview_css_class' => $this->translator->trans('ngrm.edit.vue.preview.css_class', [], 'ngremotemedia'),
'preview_css_class_info' => $this->translator->trans('ngrm.edit.vue.preview.css_class_info', [], 'ngremotemedia'),
'preview_selected_variation' => $this->translator->trans('ngrm.edit.vue.preview.selected_variation', [], 'ngremotemedia'),
'preview_selected_variation_info' => $this->translator->trans('ngrm.edit.vue.preview.selected_variation_info', [], 'ngremotemedia'),
'preview_size' => $this->translator->trans('ngrm.edit.vue.preview.size', [], 'ngremotemedia'),
'upload_modal_title' => $this->translator->trans('ngrm.edit.vue.upload.modal_title', [], 'ngremotemedia'),
'upload_breadcrumbs_info' => $this->translator->trans('ngrm.edit.vue.upload.breadcrumbs_info', [], 'ngremotemedia'),
'upload_root_folder' => $this->translator->trans('ngrm.edit.vue.upload.root_folder', [], 'ngremotemedia'),
'upload_info_text' => $this->translator->trans('ngrm.edit.vue.upload.info_text', [], 'ngremotemedia'),
'upload_button_select' => $this->translator->trans('ngrm.edit.vue.upload.button.select', [], 'ngremotemedia'),
'upload_button_create' => $this->translator->trans('ngrm.edit.vue.upload.button.create', [], 'ngremotemedia'),
'upload_button_upload' => $this->translator->trans('ngrm.edit.vue.upload.button.upload', [], 'ngremotemedia'),
'upload_button_use_existing_resource' => $this->translator->trans('ngrm.edit.vue.upload.button.use_existing_resource', [], 'ngremotemedia'),
'upload_checkbox_overwrite' => $this->translator->trans('ngrm.edit.vue.upload.checkbox.overwrite', [], 'ngremotemedia'),
'upload_placeholder_new_folder' => $this->translator->trans('ngrm.edit.vue.upload.placeholder.new_folder', [], 'ngremotemedia'),
'upload_error_existing_resource' => $this->translator->trans('ngrm.edit.vue.upload.error.existing_resource', [], 'ngremotemedia'),
'upload_error_unsupported_resource_type' => $this->translator->trans('ngrm.edit.vue.upload.error.unsupported_resource_type', [], 'ngremotemedia'),
];
}

private function resolveAvailableVariations(Request $request): array
{
$variationGroup = $request->query->get('variationGroup');

$variations = $this->variationResolver->getAvailableCroppableVariations($variationGroup);

$availableVariations = [];
foreach ($variations as $variationName => $variationConfig) {
foreach ($variationConfig['transformations'] as $name => $config) {
if ($name !== 'crop') {
continue;
}

$availableVariations[$variationName] = $config;
}
}

return $availableVariations;
}

private function resolveAllVariations(Request $request): array
{
$variationGroup = $request->query->get('variationGroup');

$variations = $this->variationResolver->getAvailableVariations($variationGroup);

return array_keys($variations);
}

/**
* @return array<string, string>
*/
private function resolveUploadContext(Request $request): array
{
return $request->query->get('uploadContext');
}
}
54 changes: 54 additions & 0 deletions bundle/Controller/Location/Create.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace Netgen\Bundle\RemoteMediaBundle\Controller\Location;

use InvalidArgumentException;
use Netgen\RemoteMedia\API\ProviderInterface;
use Netgen\RemoteMedia\API\Values\RemoteResourceLocation;
use Netgen\RemoteMedia\Exception\RemoteResourceNotFoundException;
use Netgen\RemoteMedia\Service\RemoteResourceService;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

use function json_decode;

final class Create
{
public function __construct(
private ProviderInterface $provider,
private RemoteResourceService $service,
) {}

public function __invoke(Request $request): Response
{
$selectedImage = json_decode($request->getContent(), true);

if ($selectedImage['id'] === null) {
throw new InvalidArgumentException('No image selected.');
}

try {
$remoteResource = $this->provider->loadByRemoteId($selectedImage['id']);
} catch (RemoteResourceNotFoundException $e) {
$remoteResource = $this->provider->loadFromRemote($selectedImage['id']);
}

$this->service->handleRemoteUpdate(
$remoteResource,
[
'altText' => $selectedImage['alternateText'],
'caption' => $selectedImage['caption'],
'tags' => $selectedImage['tags'],
],
true,
);

$remoteResourceLocation = new RemoteResourceLocation($remoteResource);
$this->service->handleLocationUpdate($remoteResourceLocation, $selectedImage, true);

return new JsonResponse(['locationId' => $remoteResourceLocation->getId()]);
}
}
22 changes: 22 additions & 0 deletions bundle/Controller/Location/Delete.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Netgen\Bundle\RemoteMediaBundle\Controller\Location;

use Netgen\RemoteMedia\API\ProviderInterface;
use Symfony\Component\HttpFoundation\Response;

final class Delete
{
public function __construct(
private ProviderInterface $provider,
) {}

public function __invoke(int $locationId): Response
{
$this->provider->removeLocation($this->provider->loadLocation($locationId));

return new Response();
}
}
64 changes: 64 additions & 0 deletions bundle/Controller/Location/SelectedImage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

namespace Netgen\Bundle\RemoteMediaBundle\Controller\Location;

use Netgen\RemoteMedia\API\ProviderInterface;
use Netgen\RemoteMedia\API\Values\AuthToken;
use Netgen\RemoteMedia\Service\RemoteResourceService;
use Symfony\Component\HttpFoundation\JsonResponse;

final class SelectedImage
{
public function __construct(
private ProviderInterface $provider,
private RemoteResourceService $service,
) {}

public function __invoke(int $locationId): JsonResponse
{
$remoteResourceLocation = $this->provider->loadLocation($locationId);
$remoteResource = $remoteResourceLocation->getRemoteResource();

$authenticatedLocation = $remoteResourceLocation;
$variationGroup = 'ngrm_interface';
$browseVariationName = 'browse';
$previewVariationName = 'preview';

if ($remoteResource->isProtected()) {
$token = AuthToken::fromDuration(600);
$authenticatedLocation = $this->provider->authenticateRemoteResourceLocation($remoteResourceLocation, $token);
$browseVariationName = 'browse_protected';
$previewVariationName = 'preview_protected';
}

$browseUrl = null;
$previewUrl = null;
if ($remoteResource->getType() === 'image') {
$browseUrl = $this->provider->buildVariation($authenticatedLocation, $variationGroup, $browseVariationName);
$previewUrl = $this->provider->buildVariation($authenticatedLocation, $variationGroup, $previewVariationName);
} elseif ($remoteResource->getType() === 'video') {
$browseUrl = $this->provider->buildVideoThumbnailVariation($authenticatedLocation, $variationGroup, $browseVariationName);
$previewUrl = $this->provider->buildVideoThumbnailVariation($authenticatedLocation, $variationGroup, $previewVariationName);
}

return new JsonResponse([
'id' => $remoteResource->getRemoteId() ?? '',
'name' => $remoteResource->getName() ?? '',
'type' => $remoteResource->getType() ?? '',
'format' => $remoteResource->getMetadataProperty('format') ?? '',
'url' => $remoteResource->getUrl(),
'browse_url' => $browseUrl?->getUrl() ?? '',
'previewUrl' => $previewUrl?->getUrl() ?? '',
'alternateText' => $remoteResource->getAltText(),
'caption' => $remoteResource->getCaption(),
'watermarkText' => $remoteResourceLocation->getWatermarkText(),
'tags' => $remoteResource->getTags(),
'size' => $remoteResource->getSize(),
'variations' => $this->service->resolveCropSettingsJson($remoteResourceLocation),
'height' => $remoteResource->getMetadataProperty('height') ?? 0,
'width' => $remoteResource->getMetadataProperty('width') ?? 0,
]);
}
}
49 changes: 49 additions & 0 deletions bundle/Controller/Location/Update.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace Netgen\Bundle\RemoteMediaBundle\Controller\Location;

use InvalidArgumentException;
use Netgen\RemoteMedia\API\ProviderInterface;
use Netgen\RemoteMedia\Service\RemoteResourceService;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

use function json_decode;

final class Update
{
public function __construct(
private ProviderInterface $provider,
private RemoteResourceService $service,
) {}

public function __invoke(int $locationId, Request $request): Response
{
$selectedImage = json_decode($request->getContent(), true);

if ($selectedImage['id'] === null) {
throw new InvalidArgumentException('No selected image data.');
}

$remoteResourceLocation = $this->provider->loadLocation($locationId);
if ($remoteResourceLocation->getRemoteResource()->getRemoteId() !== $selectedImage['id']) {
throw new InvalidArgumentException('Trying to update location with new resource. Instead, delete this location and create a new one.');
}

$this->service->handleRemoteUpdate(
$remoteResourceLocation->getRemoteResource(),
[
'altText' => $selectedImage['alternateText'],
'caption' => $selectedImage['caption'],
'tags' => $selectedImage['tags'],
],
true,
);

$this->service->handleLocationUpdate($remoteResourceLocation, $selectedImage, true);

return new Response();
}
}
Loading