diff --git a/composer.json b/composer.json index bce3213..c4cde87 100644 --- a/composer.json +++ b/composer.json @@ -3,11 +3,18 @@ "license": "proprietary", "minimum-stability": "stable", "prefer-stable": true, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/afornerot/bNine-FilesBundle" + } + ], "require": { "php": ">=8.2", "ext-ctype": "*", "ext-iconv": "*", "apereo/phpcas": "^1.6", + "bnine/files-bundle": "^1.0", "doctrine/dbal": "^3", "doctrine/doctrine-bundle": "^2.13", "doctrine/doctrine-migrations-bundle": "^3.3", diff --git a/composer.lock b/composer.lock index 3587b19..8b4759a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "236661a47d3b0278e1198c4fb0940e3a", + "content-hash": "607d2ea75ad35d6ff9e6f5cd12d12c62", "packages": [ { "name": "apereo/phpcas", @@ -77,6 +77,40 @@ }, "time": "2023-02-19T19:52:35+00:00" }, + { + "name": "bnine/files-bundle", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/afornerot/bNine-FilesBundle.git", + "reference": "f3a0b55f9b64930bb8cb9fa53dd7cb8799796014" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/afornerot/bNine-FilesBundle/zipball/f3a0b55f9b64930bb8cb9fa53dd7cb8799796014", + "reference": "f3a0b55f9b64930bb8cb9fa53dd7cb8799796014", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/framework-bundle": "^6.0 || ^7.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Bnine\\FilesBundle\\": "src/" + } + }, + "license": [ + "MIT" + ], + "description": "Symfony bundle for file-browser", + "support": { + "source": "https://github.com/afornerot/bNine-FilesBundle/tree/v1.0.1", + "issues": "https://github.com/afornerot/bNine-FilesBundle/issues" + }, + "time": "2025-07-30T20:35:03+00:00" + }, { "name": "brick/math", "version": "0.13.1", diff --git a/config/bundles.php b/config/bundles.php index 368f58b..bdd8cf7 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -15,4 +15,5 @@ return [ Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], Oneup\UploaderBundle\OneupUploaderBundle::class => ['all' => true], FOS\RestBundle\FOSRestBundle::class => ['all' => true], + Bnine\FilesBundle\BnineFilesBundle::class => ['all' => true], ]; diff --git a/src/Controller/FileController.php b/src/Controller/FileController.php deleted file mode 100644 index 4e7c0a4..0000000 --- a/src/Controller/FileController.php +++ /dev/null @@ -1,109 +0,0 @@ -fileService = $fileService; - } - - #[Route('/user/file/{domain}/{id}/{editable}', name: 'app_files', methods: ['GET'])] - public function browse(string $domain, int $id, int $editable, Request $request): Response - { - $relativePath = $request->query->get('path', ''); - - try { - $files = $this->fileService->list($domain, (string) $id, $relativePath); - - return $this->render('file/browse.html.twig', [ - 'domain' => $domain, - 'id' => $id, - 'files' => $files, - 'path' => $relativePath, - 'editable' => $editable, - ]); - } catch (\Exception $e) { - $this->addFlash('danger', $e->getMessage()); - dd($e->getMessage()); - - return $this->redirectToRoute('app_files', [ - 'domain' => $domain, - 'id' => $id, - 'editable' => $editable, - ]); - } - } - - #[Route('/user/uploadmodal/{domain}/{id}', name: 'app_files_uploadmodal', methods: ['GET'])] - public function uploadmodal(string $domain, int $id, Request $request): Response - { - $relativePath = $request->query->get('path', ''); - - return $this->render('file\upload.html.twig', [ - 'useheader' => false, - 'usemenu' => false, - 'usesidebar' => false, - 'endpoint' => 'file', - 'domain' => $domain, - 'id' => $id, - 'path' => $relativePath, - ]); - } - - #[Route('/user/uploadfile', name: 'app_files_uploadfile', methods: ['POST'])] - public function upload(Request $request): Response|ResponseInterface - { - /** @var UploadedFile $file */ - $file = $request->files->get('file'); - $domain = $request->query->get('domain'); - $id = $request->query->get('id'); - $relativePath = $request->query->get('path', ''); - - if (!$file || !$domain || !$id) { - return new Response('Invalid parameters', 400); - } - - $baseDir = $this->getParameter('kernel.project_dir').'/uploads/'.$domain.'/'.$id.'/'.ltrim($relativePath, '/'); - - if (!is_dir($baseDir)) { - mkdir($baseDir, 0775, true); - } - - $originalName = $file->getClientOriginalName(); - $file->move($baseDir, $originalName); - - return new JsonResponse(['success' => true]); - } - - #[Route('/user/file/{domain}/{id}/delete', name: 'app_files_delete', methods: ['POST'])] - public function delete(string $domain, int $id, Request $request): JsonResponse - { - $data = json_decode($request->getContent(), true); - $relativePath = $data['path'] ?? null; - - if (!$relativePath) { - return $this->json(['error' => 'Chemin non fourni.'], 400); - } - - try { - $this->fileService->delete($domain, (string) $id, $relativePath); - - return $this->json(['success' => true]); - } catch (\Exception $e) { - return $this->json(['error' => $e->getMessage()], 400); - } - } -} diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php index 90de8b5..0b6a696 100644 --- a/src/Controller/ProjectController.php +++ b/src/Controller/ProjectController.php @@ -6,7 +6,7 @@ use App\Entity\Project; use App\Entity\User; use App\Form\ProjectType; use App\Repository\ProjectRepository; -use App\Service\FileService; +use Bnine\FilesBundle\Service\FileService; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Service/FileService.php b/src/Service/FileService.php deleted file mode 100644 index 5981246..0000000 --- a/src/Service/FileService.php +++ /dev/null @@ -1,101 +0,0 @@ -filesystem = new Filesystem(); - $projectDir = $kernel->getProjectDir(); // chemin racine du projet - $this->basePath = $projectDir.'/uploads'; - - if (!is_dir($this->basePath)) { - // On crée le dossier uploads s'il n'existe pas - try { - $this->filesystem->mkdir($this->basePath, 0775); - } catch (IOExceptionInterface $e) { - throw new \RuntimeException('Impossible de créer le dossier /uploads : '.$e->getMessage()); - } - } - } - - /** - * Initialise un répertoire pour une entité (ex: project/123) - */ - public function init(string $domain, string $id): void - { - $entityPath = $this->getEntityPath($domain, $id); - if (!is_dir($entityPath)) { - try { - $this->filesystem->mkdir($entityPath, 0775); - } catch (IOExceptionInterface $e) { - throw new \RuntimeException(sprintf('Impossible de créer le répertoire pour %s/%s : %s', $domain, $id, $e->getMessage())); - } - } - } - - /** - * Liste les fichiers d’un répertoire lié à une entité (ex: project/123) - */ - public function list(string $domain, string $id, string $relativePath = ''): array - { - $targetPath = $this->getEntityPath($domain, $id).'/'.ltrim($relativePath, '/'); - $realPath = realpath($targetPath); - - $baseEntityPath = $this->getEntityPath($domain, $id); - if (!$realPath || !str_starts_with($realPath, $baseEntityPath)) { - throw new NotFoundHttpException('Répertoire non autorisé ou inexistant.'); - } - - $finder = new Finder(); - $finder->depth('== 0')->in($realPath); - - $results = []; - foreach ($finder as $file) { - $results[] = [ - 'name' => $file->getFilename(), - 'isDirectory' => $file->isDir(), - 'path' => ltrim(str_replace($baseEntityPath, '', $file->getRealPath()), '/'), - ]; - } - - return $results; - } - - /** - * Supprime un fichier ou dossier (de façon sécurisée) - */ - public function delete(string $domain, string $id, string $relativePath): void - { - $baseEntityPath = $this->getEntityPath($domain, $id); - $targetPath = realpath($baseEntityPath.'/'.ltrim($relativePath, '/')); - - if (!$targetPath || !str_starts_with($targetPath, $baseEntityPath)) { - throw new NotFoundHttpException('Fichier ou dossier non autorisé.'); - } - - try { - $this->filesystem->remove($targetPath); - } catch (IOExceptionInterface $e) { - throw new \RuntimeException('Erreur lors de la suppression : '.$e->getMessage()); - } - } - - /** - * Construit le chemin absolu d’un domaine/id - */ - private function getEntityPath(string $domain, string $id): string - { - return $this->basePath.'/'.$domain.'/'.$id; - } -} diff --git a/symfony.lock b/symfony.lock index 6582239..c368837 100644 --- a/symfony.lock +++ b/symfony.lock @@ -1,4 +1,7 @@ { + "bnine/files-bundle": { + "version": "v1.0.1" + }, "doctrine/deprecations": { "version": "1.1", "recipe": { diff --git a/templates/file/browse.html.twig b/templates/file/browse.html.twig deleted file mode 100644 index f6f6164..0000000 --- a/templates/file/browse.html.twig +++ /dev/null @@ -1,124 +0,0 @@ -
- -
-
Fichiers
-
- - {% if editable %} -
- Upload -
- {% endif %} - -

Chemin : {{ path ?: '/' }}

- - {% set parentPath = path|split('/')|slice(0, -1)|join('/') %} - -
    - {% if path %} -
  • ⬅️ ..
  • - {% endif %} - - {% for file in files %} -
  • - {% if file.isDirectory %} - 📁 {{ file.name }}/ - {% if editable %} - - {% endif %} - {% else %} - 📄 {{ file.name }} - {% if editable %} - - {% endif %} - {% endif %} -
  • - {% else %} -
  • Dossier vide
  • - {% endfor %} -
-
-
- - - - - - -
diff --git a/templates/file/upload.html.twig b/templates/file/upload.html.twig deleted file mode 100644 index 2da499f..0000000 --- a/templates/file/upload.html.twig +++ /dev/null @@ -1,45 +0,0 @@ -{% extends 'base.html.twig' %} - -{% block localstyle %} - -{% endblock %} - -{% block body %} - Annuler -
-{% endblock %} - -{% block localscript %} - -{% endblock %} diff --git a/templates/project/edit.html.twig b/templates/project/edit.html.twig index 0da65f1..6f290a4 100644 --- a/templates/project/edit.html.twig +++ b/templates/project/edit.html.twig @@ -35,7 +35,7 @@ {% if mode=="update" %} - {{ render(path("app_files",{domain:'project',id:project.id, editable:1})) }} + {{ render(path("bninefiles_files",{domain:'project',id:project.id, editable:1})) }} {% endif %} {{ form_end(form) }}