From 90d7fbcbfca88b96a5dc1933fac50173431f71b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Fr=C3=A9mont?= Date: Wed, 13 May 2026 17:11:21 +0200 Subject: [PATCH 1/2] feat(Sylius): Move a directory --- .../config/app/twig_hooks/media/index.php | 7 ++ .../Admin/Controller/MediaAdminController.php | 38 ++++++++++ .../Sylius/src/Admin/Grid/MediaGrid.php | 4 ++ .../grid/action/create_directory.html.twig | 18 +++-- .../grid/action/delete_directory.html.twig | 2 +- .../grid/action/move_directory.html.twig | 26 +++++++ .../grid/action/rename_directory.html.twig | 20 ++++-- .../modal/move_directory_modal.html.twig | 5 ++ .../actions/move_directory.html.twig | 22 ++++++ .../helper/move_directory_modal.html.twig | 71 +++++++++++++++++++ .../JoliMediaSyliusBundle.en.yaml | 2 + .../JoliMediaSyliusBundle.fr.yaml | 2 + .../src/Twig/JoliMediaAdminExtension.php | 1 + 13 files changed, 209 insertions(+), 9 deletions(-) create mode 100644 src/Bridge/Sylius/templates/admin/media/grid/action/move_directory.html.twig create mode 100644 src/Bridge/Sylius/templates/admin/media/index/modal/move_directory_modal.html.twig create mode 100644 src/Bridge/Sylius/templates/admin/media/show/content/header/title_block/actions/move_directory.html.twig create mode 100644 src/Bridge/Sylius/templates/admin/shared/helper/move_directory_modal.html.twig diff --git a/src/Bridge/Sylius/config/app/twig_hooks/media/index.php b/src/Bridge/Sylius/config/app/twig_hooks/media/index.php index b17584ab..73735fca 100644 --- a/src/Bridge/Sylius/config/app/twig_hooks/media/index.php +++ b/src/Bridge/Sylius/config/app/twig_hooks/media/index.php @@ -7,6 +7,13 @@ return static function (ContainerConfigurator $container): void { $container->extension('sylius_twig_hooks', [ 'hooks' => [ + 'joli_media_sylius_admin.media.index' => [ + 'modal' => [ + 'template' => '@JoliMediaSylius/admin/media/index/modal/move_directory_modal.html.twig', + 'priority' => 100, + ], + ], + 'joli_media_sylius_admin.media.index.content' => [ 'header' => [ 'template' => '@JoliMediaSylius/admin/media/index/content/header.html.twig', diff --git a/src/Bridge/Sylius/src/Admin/Controller/MediaAdminController.php b/src/Bridge/Sylius/src/Admin/Controller/MediaAdminController.php index 1e917c28..2e15cfce 100644 --- a/src/Bridge/Sylius/src/Admin/Controller/MediaAdminController.php +++ b/src/Bridge/Sylius/src/Admin/Controller/MediaAdminController.php @@ -151,6 +151,44 @@ public function renameDirectory(Request $request): Response } } + #[Route(path: '/move-directory', name: 'move_directory', methods: [Request::METHOD_POST])] + public function moveDirectory(Request $request): Response + { + $from = Resolver::normalizePath($request->request->getString('from')); + $to = Resolver::normalizePath($request->request->getString('to')); + + $csrfToken = $request->request->getString('_csrf_token'); + + if (!$this->csrfTokenManager->isTokenValid(new CsrfToken('media_move_directory', $csrfToken))) { + $this->addFlash('error', 'Invalid CSRF token'); + + return $this->redirect($request->headers->get('referer') ?? $this->generateUrl('joli_media_sylius_admin_explore')); + } + + if (!$from) { + $this->addFlash('error', 'Missing parameters'); + + return $this->redirect($request->headers->get('referer') ?? $this->generateUrl('joli_media_sylius_admin_explore')); + } + + try { + $target = $to ? \sprintf('%s/%s', $to, basename($from)) : basename($from); + $this->getOriginalStorage()->moveFolder($from, $target); + + $this->addFlash('success', $this->translator->trans( + 'directory.move_success', + ['%from%' => $from, '%to%' => $target], + 'JoliMediaSyliusBundle' + )); + + return $this->redirectAfterRename($request, $from, $target); + } catch (\Throwable $e) { + $this->addFlash('error', $e->getMessage()); + + return $this->redirect($request->headers->get('referer') ?? $this->generateUrl('joli_media_sylius_admin_explore')); + } + } + #[Route(path: '/rename', name: 'rename', methods: [Request::METHOD_POST])] public function rename(Request $request): Response { diff --git a/src/Bridge/Sylius/src/Admin/Grid/MediaGrid.php b/src/Bridge/Sylius/src/Admin/Grid/MediaGrid.php index b410e72e..5298c3df 100644 --- a/src/Bridge/Sylius/src/Admin/Grid/MediaGrid.php +++ b/src/Bridge/Sylius/src/Admin/Grid/MediaGrid.php @@ -72,6 +72,10 @@ public function __invoke(GridBuilderInterface $gridBuilder): void ->setLabel($this->trans('action.rename_directory')) ->setIcon('tabler:pencil') ->setTemplate('@JoliMediaSylius/admin/media/grid/action/rename_directory.html.twig'), + Action::create('move_directory', 'custom') + ->setLabel($this->trans('action.move_directory')) + ->setIcon('fa7-solid:folder-tree') + ->setTemplate('@JoliMediaSylius/admin/media/grid/action/move_directory.html.twig'), Action::create('create_directory', 'custom') ->setLabel($this->trans('directory.create')) ->setIcon('tabler:folder-plus') diff --git a/src/Bridge/Sylius/templates/admin/media/grid/action/create_directory.html.twig b/src/Bridge/Sylius/templates/admin/media/grid/action/create_directory.html.twig index 7b6f418a..4e8bad21 100644 --- a/src/Bridge/Sylius/templates/admin/media/grid/action/create_directory.html.twig +++ b/src/Bridge/Sylius/templates/admin/media/grid/action/create_directory.html.twig @@ -1,9 +1,19 @@ +{% import ['@SyliusBootstrapAdminUi/shared/helper/button.html.twig', '@SyliusAdmin/shared/helper/button.html.twig'] as button %} + {% set message = action.label %} {% if message is empty %} {% set message = 'sylius.ui.create' %} {% endif %} - - {{ ux_icon(action.icon|default('tabler:plus'), {'class': 'icon dropdown-item-icon'}) }} - {{ message|trans }} - +{{ button.default({ + url: '#', + icon: action.icon|default('tabler:plus'), + icon_class: 'icon dropdown-item-icon', + icon_only: true, + text: message|trans, + attr: { + 'data-bs-toggle': 'tooltip', + 'data-bs-title': message|trans, + 'data-component': 'directory-create' + } +}) }} diff --git a/src/Bridge/Sylius/templates/admin/media/grid/action/delete_directory.html.twig b/src/Bridge/Sylius/templates/admin/media/grid/action/delete_directory.html.twig index 1805b167..bc0d35da 100644 --- a/src/Bridge/Sylius/templates/admin/media/grid/action/delete_directory.html.twig +++ b/src/Bridge/Sylius/templates/admin/media/grid/action/delete_directory.html.twig @@ -8,7 +8,7 @@ id: key, modal_id: 'delete-directory-modal-' ~ random(), path: path('joli_media_sylius_admin_delete_directory', {'key': key}), - icon_only: false, + icon_only: true, label: action.label, }) }} {% endif %} diff --git a/src/Bridge/Sylius/templates/admin/media/grid/action/move_directory.html.twig b/src/Bridge/Sylius/templates/admin/media/grid/action/move_directory.html.twig new file mode 100644 index 00000000..7cdb23bd --- /dev/null +++ b/src/Bridge/Sylius/templates/admin/media/grid/action/move_directory.html.twig @@ -0,0 +1,26 @@ +{% import ['@SyliusBootstrapAdminUi/shared/helper/button.html.twig', '@SyliusAdmin/shared/helper/button.html.twig'] as button %} + +{% set key = app.request.attributes.get('key', '') %} + +{% if key != '' %} + {% set message = action.label %} + {% if message is empty %} + {% set message = 'sylius.ui.update' %} + {% endif %} + + + {{ button.default({ + url: path('joli_media_sylius_admin_choose'), + icon: action.icon|default('fa7-solid:folder-tree'), + icon_class: 'icon dropdown-item-icon', + icon_only: true, + text: message|trans, + attr: { + 'data-bs-toggle': 'tooltip', + 'data-bs-title': message|trans, + 'data-folder': key|joli_media_admin_dirname, + 'data-component': 'media-move' + } + }) }} + +{% endif %} diff --git a/src/Bridge/Sylius/templates/admin/media/grid/action/rename_directory.html.twig b/src/Bridge/Sylius/templates/admin/media/grid/action/rename_directory.html.twig index 4e072229..41c71fe2 100644 --- a/src/Bridge/Sylius/templates/admin/media/grid/action/rename_directory.html.twig +++ b/src/Bridge/Sylius/templates/admin/media/grid/action/rename_directory.html.twig @@ -1,3 +1,5 @@ +{% import ['@SyliusBootstrapAdminUi/shared/helper/button.html.twig', '@SyliusAdmin/shared/helper/button.html.twig'] as button %} + {% set key = app.request.attributes.get('key', '') %} {% if key != '' %} @@ -6,8 +8,18 @@ {% set message = 'sylius.ui.update' %} {% endif %} - - {{ ux_icon(action.icon|default('tabler:pencil'), {'class': 'icon dropdown-item-icon'}) }} - {{ message|trans }} - + {{ button.default({ + url: '#', + icon: action.icon|default('tabler:pencil'), + icon_class: 'icon dropdown-item-icon', + icon_only: true, + text: message|trans, + class: 'directory-rename-header-btn', + attr: { + 'data-bs-toggle': 'tooltip', + 'data-bs-title': message|trans, + 'data-component': 'directory-rename' + } + }) }} {% endif %} + diff --git a/src/Bridge/Sylius/templates/admin/media/index/modal/move_directory_modal.html.twig b/src/Bridge/Sylius/templates/admin/media/index/modal/move_directory_modal.html.twig new file mode 100644 index 00000000..7e2b77f6 --- /dev/null +++ b/src/Bridge/Sylius/templates/admin/media/index/modal/move_directory_modal.html.twig @@ -0,0 +1,5 @@ +{% import '@JoliMediaSylius/admin/shared/helper/move_directory_modal.html.twig' as modal %} + +{% set directory = hookable_metadata.context.key %} + +{{ modal.render(directory) }} diff --git a/src/Bridge/Sylius/templates/admin/media/show/content/header/title_block/actions/move_directory.html.twig b/src/Bridge/Sylius/templates/admin/media/show/content/header/title_block/actions/move_directory.html.twig new file mode 100644 index 00000000..3704e335 --- /dev/null +++ b/src/Bridge/Sylius/templates/admin/media/show/content/header/title_block/actions/move_directory.html.twig @@ -0,0 +1,22 @@ +{% trans_default_domain 'JoliMediaSyliusBundle' %} + +{% set media = hookable_metadata.context.media %} + + + + {{ ux_icon('fa7-solid:folder-tree') }} + {{ 'action.move_directory'|trans }} + + + +
+ + + +
diff --git a/src/Bridge/Sylius/templates/admin/shared/helper/move_directory_modal.html.twig b/src/Bridge/Sylius/templates/admin/shared/helper/move_directory_modal.html.twig new file mode 100644 index 00000000..a16670c4 --- /dev/null +++ b/src/Bridge/Sylius/templates/admin/shared/helper/move_directory_modal.html.twig @@ -0,0 +1,71 @@ +{% macro render(directoryPath) %} + {% trans_default_domain 'JoliMediaSyliusBundle' %} + + + +
+ + + +
+{% endmacro %} diff --git a/src/Bridge/Sylius/translations/JoliMediaSyliusBundle.en.yaml b/src/Bridge/Sylius/translations/JoliMediaSyliusBundle.en.yaml index dd41e873..958557f9 100644 --- a/src/Bridge/Sylius/translations/JoliMediaSyliusBundle.en.yaml +++ b/src/Bridge/Sylius/translations/JoliMediaSyliusBundle.en.yaml @@ -10,6 +10,7 @@ action: rename_directory: 'Rename directory' cancel: 'Cancel' entity_actions: 'Actions' + move_directory: 'Move directory' move_file: 'Move this file' move_to_this_directory: 'Move to this directory' search: 'Search' @@ -26,6 +27,7 @@ directory: name: 'Directory name' name_required: 'Directory name is required' rename_success: 'The directory "%from%" was successfully renamed to "%to%"' + move_success: 'The directory "%from%" was successfully moved to "%to%".' media: add: 'Add media' dimensions: 'Dimensions' diff --git a/src/Bridge/Sylius/translations/JoliMediaSyliusBundle.fr.yaml b/src/Bridge/Sylius/translations/JoliMediaSyliusBundle.fr.yaml index 481b7b63..d67768ff 100644 --- a/src/Bridge/Sylius/translations/JoliMediaSyliusBundle.fr.yaml +++ b/src/Bridge/Sylius/translations/JoliMediaSyliusBundle.fr.yaml @@ -10,6 +10,7 @@ action: rename_directory: 'Renommer le dossier' cancel: 'Annuler' entity_actions: 'Actions' + move_directory: 'Déplacer ce dossier' move_file: 'Déplacer ce fichier' move_to_this_directory: 'Déplacer dans ce répertoire' search: 'Rechercher' @@ -25,6 +26,7 @@ directory: create_failure: 'Échec de la création du répertoire "%directory%" : %error%' name_required: 'Le nom du répertoire est requis' rename_success: 'Le répertoire %from% a été renommé avec succès en %to%.' + move_success: 'Le répertoire "%from%" a été déplacé avec succès vers "%to%".' media: add: 'Ajouter un média' dimensions: 'Dimensions' diff --git a/src/Bridge/src/Twig/JoliMediaAdminExtension.php b/src/Bridge/src/Twig/JoliMediaAdminExtension.php index cf876c08..4085b18e 100644 --- a/src/Bridge/src/Twig/JoliMediaAdminExtension.php +++ b/src/Bridge/src/Twig/JoliMediaAdminExtension.php @@ -21,6 +21,7 @@ public function __construct( public function getFilters(): array { return [ + new TwigFilter('joli_media_admin_dirname', 'dirname'), new TwigFilter('joli_media_admin_basename', 'basename'), new TwigFilter('joli_media_admin_readable_filesize', $this->getReadableFileSize(...)), ]; From 57b6bcd556370cf3d76312b4df867ab911635b4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Fr=C3=A9mont?= Date: Fri, 22 May 2026 11:40:32 +0200 Subject: [PATCH 2/2] Add a PHPUnit test --- .../actions/move_directory.html.twig | 22 -------------- .../Controller/MediaAdminControllerTest.php | 30 +++++++++++++++++++ 2 files changed, 30 insertions(+), 22 deletions(-) delete mode 100644 src/Bridge/Sylius/templates/admin/media/show/content/header/title_block/actions/move_directory.html.twig diff --git a/src/Bridge/Sylius/templates/admin/media/show/content/header/title_block/actions/move_directory.html.twig b/src/Bridge/Sylius/templates/admin/media/show/content/header/title_block/actions/move_directory.html.twig deleted file mode 100644 index 3704e335..00000000 --- a/src/Bridge/Sylius/templates/admin/media/show/content/header/title_block/actions/move_directory.html.twig +++ /dev/null @@ -1,22 +0,0 @@ -{% trans_default_domain 'JoliMediaSyliusBundle' %} - -{% set media = hookable_metadata.context.media %} - - - - {{ ux_icon('fa7-solid:folder-tree') }} - {{ 'action.move_directory'|trans }} - - - -
- - - -
diff --git a/tests/Bridge/Sylius/Admin/Controller/MediaAdminControllerTest.php b/tests/Bridge/Sylius/Admin/Controller/MediaAdminControllerTest.php index d1be9c4a..68ab222b 100644 --- a/tests/Bridge/Sylius/Admin/Controller/MediaAdminControllerTest.php +++ b/tests/Bridge/Sylius/Admin/Controller/MediaAdminControllerTest.php @@ -515,6 +515,29 @@ public function testCreateDirectoryFailsWhenEmptyName(): void $this->assertSelectorTextContains('[data-test-sylius-flash-message]', 'Directory name is required'); } + public function testMoveDirectoryFromSubdirectoryToRoot(): void + { + $crawler = $this->client->request(Request::METHOD_GET, '/sylius-admin/media/explore/sub/folder'); + $this->assertResponseIsSuccessful(); + + $form = $this->getMoveDirectoryForm($crawler); + + $phpValues = $form->getPhpValues(); + $phpValues['to'] = ''; + + $this->client->request($form->getMethod(), $form->getUri(), $phpValues); + + $this->assertResponseRedirects('/sylius-admin/media/explore/folder'); + + // I should be redirected to the new directory path + $this->client->followRedirect(); + $this->assertResponseIsSuccessful(); + + // Test flash message + $this->assertSelectorExists('[data-test-sylius-flash-message]'); + $this->assertSelectorTextContains('[data-test-sylius-flash-message]', 'The directory "sub/folder" was successfully moved to "folder".'); + } + protected static function getKernelClass(): string { return Kernel::class; @@ -561,6 +584,13 @@ private function getCreateDirectoryForm(Crawler $crawler): Form return $crawler->filter('form[data-component="directory-create-form"]')->form(); } + private function getMoveDirectoryForm(Crawler $crawler): Form + { + $this->assertSelectorExists('form[id="move-form"]'); + + return $crawler->filter('form[id="move-form"]')->form(); + } + private function createTemporaryFile(): string { return tempnam(sys_get_temp_dir(), 'upload-test');