Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
57 changes: 55 additions & 2 deletions src/Bridge/Sylius/assets/js/components/mediaSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,51 @@ const configureMediaChoiceContainer = (mediaChoiceContainer) => {
};

let currentFolderPath = getCurrentFolderPath();
let currentSearchValue = '';

const getSearchUrl = (baseUrl) => {
if (!currentSearchValue) return baseUrl;
const separator = baseUrl.includes('?') ? '&' : '?';
return `${baseUrl}${separator}search=${encodeURIComponent(currentSearchValue)}`;
};

const setupSearch = () => {
const currentModalEl = document.getElementById(`modal-media-choice_${id}`);
if (!currentModalEl) return;

const searchForm = currentModalEl.querySelector('.joli-media-search-form');
const searchInput = currentModalEl.querySelector('.joli-media-search-input');
if (!searchForm || !searchInput) return;

currentSearchValue = searchInput.value;

const newSearchForm = searchForm.cloneNode(true);
searchForm.parentNode.replaceChild(newSearchForm, searchForm);

const newInput = newSearchForm.querySelector('.joli-media-search-input');

newSearchForm.addEventListener('submit', (e) => {
e.preventDefault();
e.stopPropagation();
currentSearchValue = newInput.value.trim();
const basePath = getBasePath();
const url = currentFolderPath
? `${basePath}/${currentFolderPath}`
: basePath;
fetchFolder(getSearchUrl(url)).then(configureModal);
});

newInput.addEventListener('search', () => {
if (!newInput.value) {
currentSearchValue = '';
const basePath = getBasePath();
const url = currentFolderPath
? `${basePath}/${currentFolderPath}`
: basePath;
fetchFolder(url).then(configureModal);
}
});
};

const configureModal = (html) => {
const currentModalEl = document.getElementById(`modal-media-choice_${id}`);
Expand All @@ -143,6 +188,7 @@ const configureMediaChoiceContainer = (mediaChoiceContainer) => {
currentModalContent.innerHTML = html;
updateBreadcrumb(currentFolderPath);
setupCreateFolder();
setupSearch();
};

const closeModal = () => {
Expand Down Expand Up @@ -197,7 +243,7 @@ const getParentControllers = (element) => {
const url = currentFolderPath
? `${basePath}/${currentFolderPath}`
: basePath;
fetchFolder(url).then(configureModal);
fetchFolder(getSearchUrl(url)).then(configureModal);
};

const handleModalClick = (event) => {
Expand All @@ -212,7 +258,7 @@ const getParentControllers = (element) => {
return;
}

const href = target.attributes.href.value;
let href = target.attributes.href.value;

if (
target.dataset.mediaTemplate !== undefined &&
Expand Down Expand Up @@ -248,6 +294,7 @@ const getParentControllers = (element) => {
} else {
currentFolderPath = folderPath;
updateBreadcrumb(currentFolderPath);
href = getSearchUrl(href);
}
}

Expand Down Expand Up @@ -284,6 +331,7 @@ const getParentControllers = (element) => {
fetchFolder(editButton.href).then((html) => {
if (modalBody) modalBody.innerHTML = html;
setupCreateFolder();
setupSearch();
const bsModal = bootstrap.Modal.getOrCreateInstance(modalEl);
bsModal.show();
});
Expand Down Expand Up @@ -354,6 +402,11 @@ const getParentControllers = (element) => {

parentPathInput.value = currentFolderPath;

const searchInput = createForm.querySelector('.directory-create-search');
if (searchInput) {
searchInput.value = currentSearchValue;
}

fetch(createForm.action, {
method: 'POST',
body: new FormData(createForm),
Expand Down
2 changes: 1 addition & 1 deletion src/Bridge/Sylius/public/entrypoints.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"./joli-media-sylius-admin.2f351929.css"
],
"js": [
"./joli-media-sylius-admin.0ec877e4.js"
"./joli-media-sylius-admin.dc8dd6d7.js"
]
}
}
Expand Down

This file was deleted.

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Bridge/Sylius/public/manifest.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"joli-media-sylius-admin.css": "./joli-media-sylius-admin.2f351929.css",
"joli-media-sylius-admin.js": "./joli-media-sylius-admin.0ec877e4.js"
"joli-media-sylius-admin.js": "./joli-media-sylius-admin.dc8dd6d7.js"
}
52 changes: 46 additions & 6 deletions src/Bridge/Sylius/src/Admin/Controller/MediaAdminController.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public function createDirectory(Request $request): Response
$csrfToken = $request->request->getString('_csrf_token');
$parentPath = urldecode(Resolver::normalizePath($request->request->getString('parentPath')));
$name = trim($request->request->getString('name'));
$searchValue = $request->request->getString('_search', '');

if (!$this->csrfTokenManager->isTokenValid(new CsrfToken('media_create_directory', $csrfToken))) {
$this->addFlash('error', $this->translator->trans('directory.create_failure', [
Expand Down Expand Up @@ -108,7 +109,7 @@ public function createDirectory(Request $request): Response
return $this->renderFolderChoiceHtml($parentPath);
}

return $this->renderChooseHtml($parentPath);
return $this->renderChooseHtml($parentPath, $searchValue);
}

return $this->redirect($request->headers->get('referer') ?? $this->generateUrl('joli_media_sylius_admin_explore'));
Expand Down Expand Up @@ -374,6 +375,9 @@ public function choose(Request $request, string $key = ''): Response
$request->attributes->set('currentKey', $currentKey);
$parentKey = '' !== $currentKey ? (($pos = strrpos($currentKey, '/')) !== false ? substr($currentKey, 0, $pos) : '') : '';

$searchValue = $request->query->all('criteria')['search']['value'] ?? $request->query->getString('search', '');
$hasSearch = '' !== $searchValue;

Comment on lines +378 to +380
$perPage = $this->config->getPaginationSizes()[0] ?? 10;

try {
Expand All @@ -383,17 +387,32 @@ public function choose(Request $request, string $key = ''): Response
throw new ForbiddenPathException($trashPath);
}

$directories = $this->getOriginalStorage()->listDirectories($currentKey, recursive: false);
natcasesort($directories);
$dirFilter = null;
if ($hasSearch) {
$dirFilter = static fn (string $a): bool => str_contains(strtolower($a), strtolower($searchValue));
}

$directories = $this->getOriginalStorage()->listDirectories($currentKey, recursive: $hasSearch, filter: $dirFilter);

Comment on lines +390 to +396
if (!$hasSearch) {
natcasesort($directories);
}
} catch (ForbiddenPathException|PathTraversalDetected|UnableToListContents) {
$directories = [];
}

$mediaFilter = null;
if ($hasSearch) {
$mediaFilter = static fn ($media): bool => str_contains(strtolower($media->getPath()), strtolower($searchValue));
}

if ($request->isXmlHttpRequest()) {
try {
$paginatedMedias = $this->getOriginalStorage()->listMediasPaginated(
$currentKey,
recursive: $hasSearch,
perPage: $perPage,
filter: $mediaFilter,
);
$medias = $paginatedMedias['items'];
} catch (\OutOfRangeException) {
Expand All @@ -414,8 +433,10 @@ public function choose(Request $request, string $key = ''): Response
try {
$paginatedMedias = $this->getOriginalStorage()->listMediasPaginated(
$currentKey,
recursive: $hasSearch,
page: $page,
perPage: $perPage,
filter: $mediaFilter,
);
$medias = new Pagerfanta(new FixedAdapter($paginatedMedias['total'], $paginatedMedias['items']));
$medias->setCurrentPage($page);
Expand All @@ -428,6 +449,7 @@ public function choose(Request $request, string $key = ''): Response
'current_key' => $currentKey,
'directories' => $directories,
'medias' => $medias,
'search' => $searchValue,
'create_media_form' => $this->createUploadForm($currentKey)->createView(),
'config' => $this->config,
'csrf_token_create' => $this->csrfTokenManager->getToken('media_create_directory')->getValue(),
Expand Down Expand Up @@ -550,25 +572,42 @@ private function redirectAfterRename(Request $request, string $oldPath, string $
return $this->redirectToRoute('joli_media_sylius_admin_explore', ['key' => $newPath]);
}

private function renderChooseHtml(string $currentKey): Response
private function renderChooseHtml(string $currentKey, string $searchValue = ''): Response
{
$hasSearch = '' !== $searchValue;

try {
$trashPath = $this->getOriginalStorage()->getTrashPath();

if ($trashPath === $currentKey || str_starts_with($currentKey, $trashPath . '/')) {
throw new ForbiddenPathException($trashPath);
}

$directories = $this->getOriginalStorage()->listDirectories($currentKey, recursive: false);
natcasesort($directories);
$dirFilter = null;
if ($hasSearch) {
$dirFilter = static fn (string $a): bool => str_contains(strtolower($a), strtolower($searchValue));
}

$directories = $this->getOriginalStorage()->listDirectories($currentKey, recursive: $hasSearch, filter: $dirFilter);

if (!$hasSearch) {
natcasesort($directories);
}
} catch (ForbiddenPathException|PathTraversalDetected|UnableToListContents) {
$directories = [];
}

$mediaFilter = null;
if ($hasSearch) {
$mediaFilter = static fn ($media): bool => str_contains(strtolower($media->getPath()), strtolower($searchValue));
}

try {
$paginatedMedias = $this->getOriginalStorage()->listMediasPaginated(
$currentKey,
recursive: $hasSearch,
perPage: $this->config->getPaginationSizes()[0] ?? 10,
filter: $mediaFilter,
);
$medias = new Pagerfanta(new FixedAdapter($paginatedMedias['total'], $paginatedMedias['items']));
$medias->setCurrentPage(1);
Expand All @@ -581,6 +620,7 @@ private function renderChooseHtml(string $currentKey): Response
'current_key' => $currentKey,
'directories' => $directories,
'medias' => $medias,
'search' => $searchValue,
'create_media_form' => $this->createUploadForm($currentKey)->createView(),
'config' => $this->config,
'csrf_token_create' => $this->csrfTokenManager->getToken('media_create_directory')->getValue(),
Expand Down
15 changes: 13 additions & 2 deletions src/Bridge/Sylius/templates/admin/media/choose.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,24 @@
</div>
</div>

<div class="joli-media-search mb-3">
<form class="joli-media-search-form" data-component="media-search">
<label for="joli-media-search-input" class="visually-hidden">{{ 'media.search_label'|trans }}</label>
<div class="input-group">
<input id="joli-media-search-input" type="search" class="form-control joli-media-search-input" placeholder="{{ 'media.search_label'|trans }}" value="{{ search }}" autocomplete="off">
<button class="btn btn-outline-secondary joli-media-search-btn" type="submit">{{ ux_icon('tabler:search') }} {{ 'action.search'|trans }}</button>
</div>
</form>
</div>

<form class="directory-create-form d-none mt-2"
data-component="directory-create-form"
action="{{ path('joli_media_sylius_admin_create_directory') }}"
method="POST"
>
<input type="hidden" name="_csrf_token" value="{{ csrf_token_create }}">
<input type="hidden" name="parentPath" class="directory-create-parent-path" value="{{ current_key }}">
<input type="hidden" name="_search" class="directory-create-search" value="{{ search }}">
<div class="input-group input-group-sm">
<input type="text" name="name" class="form-control directory-create-input" placeholder="{{ 'directory.name'|trans }}">
<button type="submit" class="btn btn-primary">
Expand Down Expand Up @@ -87,7 +98,7 @@
{% if directories|length > 0 %}
<ul class="gallery-grid gallery-grid--folders">
{% for directory in directories %}
<li class="gallery-grid-item">
<li class="gallery-grid-item" {{ sylius_test_html_attribute('directory-row') }}>
<a href="{{ path('joli_media_sylius_admin_choose', { key: directory }) }}" class="gallery-grid-item__link">
<span class="gallery-grid-item__icon">
{{ ux_icon('tabler:folder', {class: 'w-80 h-80'}) }}
Expand All @@ -102,7 +113,7 @@
<ul class="gallery-grid gallery-grid--files">
{% for media in medias %}
{% if media.isStored %}
<li class="gallery-grid-item">
<li class="gallery-grid-item" {{ sylius_test_html_attribute('row') }}>
<a class="gallery-grid-item__link"
href="#"
data-media-folder="{{ current_key }}"
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<script src="{{ asset('bundles/jolimediasylius/joli-media-sylius-admin.0ec877e4.js') }}"></script>
<script src="{{ asset('bundles/jolimediasylius/joli-media-sylius-admin.dc8dd6d7.js') }}"></script>
2 changes: 2 additions & 0 deletions src/Bridge/Sylius/translations/JoliMediaSyliusBundle.en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ action:
entity_actions: 'Actions'
move_file: 'Move this file'
move_to_this_directory: 'Move to this directory'
search: 'Search'
browser:
audio:
unsupported: 'Your browser does not support the audio element.'
Expand Down Expand Up @@ -62,6 +63,7 @@ media:
move_success: 'The file %from% was successfully moved to %to%.'
move_failure: 'Failed to move the media from %from% to %to%. Error: %error%'
rename_success: 'The file %from% was successfully renamed to %to%.'
search_label: 'Search media...'
upload:
dropzone:
default_message: 'Drop files here to upload'
Expand Down
2 changes: 2 additions & 0 deletions src/Bridge/Sylius/translations/JoliMediaSyliusBundle.fr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ action:
entity_actions: 'Actions'
move_file: 'Déplacer ce fichier'
move_to_this_directory: 'Déplacer dans ce répertoire'
search: 'Rechercher'
browser:
audio:
unsupported: 'Votre navigateur ne supporte pas cet élément audio.'
Expand Down Expand Up @@ -61,6 +62,7 @@ media:
move_success: 'Le fichier %from% a été déplacé avec succès vers %to%.'
move_failure: 'Échec du déplacement du média de %from% vers %to%. Erreur : %error%'
rename_success: 'Le fichier %from% a été renommé avec succès en %to%.'
search_label: 'Rechercher un média...'
upload:
dropzone:
default_message: 'Déposez les fichiers ici pour les télécharger'
Expand Down
26 changes: 26 additions & 0 deletions tests/Bridge/Sylius/Admin/Controller/MediaAdminControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@ public function testFilteringDirectories(): void
$this->assertSelectorTextContains('tr.item:last-child', 'sub/folder/deep/test.txt');
}

public function testFilteringDirectoriesInChooseMediaModal(): void
{
$crawler = $this->client->request(Request::METHOD_GET, '/sylius-admin/media/choose?search=sub');
$this->assertResponseIsSuccessful();

// List of directories
$this->assertSame(3, $this->getDirectoryCount($crawler));

// List of medias
$this->assertSame(2, $this->getMediaCount($crawler));
}

public function testFilteringMedias(): void
{
$crawler = $this->client->request(Request::METHOD_GET, '/sylius-admin/media/explore?criteria[search][value]=test.txt');
Expand All @@ -83,6 +95,20 @@ public function testFilteringMedias(): void
$this->assertSelectorTextContains('tr.item:last-child', 'sub/folder/deep/test.txt');
}

public function testFilteringMediasInTheChooseMediaModal(): void
{
$crawler = $this->client->request(Request::METHOD_GET, '/sylius-admin/media/choose?search=test.txt');
$this->assertResponseIsSuccessful();

// List of directories
$this->assertSame(0, $this->getDirectoryCount($crawler));

// List of medias
$this->assertSame(2, $this->getMediaCount($crawler));
$this->assertSelectorTextContains('.gallery-grid-item:first-child', 'test.txt');
$this->assertSelectorTextContains('.gallery-grid-item:last-child', 'test.txt');
}

public function testSortingMediasByNameWithAscendingOrder(): void
{
$crawler = $this->client->request(Request::METHOD_GET, '/sylius-admin/media/explore?sorting[path]=asc');
Expand Down
Loading