vendor/artgris/filemanager-bundle/Controller/ManagerController.php line 276

  1. <?php
  2. namespace Artgris\Bundle\FileManagerBundle\Controller;
  3. use Artgris\Bundle\FileManagerBundle\Event\FileManagerEvents;
  4. use Artgris\Bundle\FileManagerBundle\Helpers\File;
  5. use Artgris\Bundle\FileManagerBundle\Helpers\FileManager;
  6. use Artgris\Bundle\FileManagerBundle\Helpers\FileManagerUploadHandler;
  7. use Artgris\Bundle\FileManagerBundle\Service\FilemanagerService;
  8. use Artgris\Bundle\FileManagerBundle\Service\FileTypeService;
  9. use Artgris\Bundle\FileManagerBundle\Twig\OrderExtension;
  10. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  11. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  12. use Symfony\Component\EventDispatcher\GenericEvent;
  13. use Symfony\Component\Filesystem\Exception\IOException;
  14. use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
  15. use Symfony\Component\Filesystem\Filesystem;
  16. use Symfony\Component\Finder\Finder;
  17. use Symfony\Component\Finder\SplFileInfo;
  18. use Symfony\Component\Form\Extension\Core\Type\FormType;
  19. use Symfony\Component\Form\Extension\Core\Type\HiddenType;
  20. use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  21. use Symfony\Component\Form\Extension\Core\Type\TextType;
  22. use Symfony\Component\Form\Form;
  23. use Symfony\Component\Form\FormFactoryInterface;
  24. use Symfony\Component\Form\FormInterface;
  25. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  26. use Symfony\Component\HttpFoundation\JsonResponse;
  27. use Symfony\Component\HttpFoundation\RedirectResponse;
  28. use Symfony\Component\HttpFoundation\Request;
  29. use Symfony\Component\HttpFoundation\Response;
  30. use Symfony\Component\Routing\Annotation\Route;
  31. use Symfony\Component\Routing\RouterInterface;
  32. use Symfony\Component\Validator\Constraints\NotBlank;
  33. use Symfony\Contracts\Translation\TranslatorInterface;
  34. use Exception;
  35. /**
  36.  * @author Arthur Gribet <a.gribet@gmail.com>
  37.  */
  38. class ManagerController extends AbstractController {
  39.     private FileManager $fileManager;
  40.     /**
  41.      * ManagerController constructor.
  42.      */
  43.     public function __construct(private FilemanagerService $filemanagerService, private EventDispatcherInterface $dispatcher, private TranslatorInterface $translator, private RouterInterface $router, private FormFactoryInterface $formFactory) {
  44.     }
  45.     #[Route('/'name'file_manager')]
  46.     public function indexAction(Request $requestFileTypeService $fileTypeService): JsonResponse|Response {
  47.         $queryParameters $request->query->all();
  48.         $isJson $request->get('json');
  49.         if ($isJson) {
  50.             unset($queryParameters['json']);
  51.         }
  52.         $fileManager $this->newFileManager($queryParameters);
  53.         // Folder search
  54.         $directoriesArbo $this->retrieveSubDirectories($fileManager$fileManager->getDirName(), \DIRECTORY_SEPARATOR$fileManager->getBaseName());
  55.         // File search
  56.         $finderFiles = new Finder();
  57.         $finderFiles->in($fileManager->getCurrentPath())->depth(0);
  58.         $regex $fileManager->getRegex();
  59.         $orderBy $fileManager->getQueryParameter('orderby');
  60.         $orderDESC OrderExtension::DESC === $fileManager->getQueryParameter('order');
  61.         if (!$orderBy) {
  62.             $finderFiles->sortByType();
  63.         }
  64.         switch ($orderBy) {
  65.             case 'name':
  66.                 $finderFiles->sort(function (SplFileInfo $aSplFileInfo $b) {
  67.                     return strcmp(mb_strtolower($b->getFilename()), mb_strtolower($a->getFilename()));
  68.                 });
  69.                 break;
  70.             case 'date':
  71.                 $finderFiles->sortByModifiedTime();
  72.                 break;
  73.             case 'size':
  74.                 $finderFiles->sort(function (\SplFileInfo $a\SplFileInfo $b) {
  75.                     return $a->getSize() - $b->getSize();
  76.                 });
  77.                 break;
  78.         }
  79.         if ($fileManager->getTree()) {
  80.             $finderFiles->files()->name($regex)->filter(function (SplFileInfo $file) {
  81.                 return $file->isReadable();
  82.             });
  83.         } else {
  84.             $finderFiles->filter(function (SplFileInfo $file) use ($regex) {
  85.                 if ('file' === $file->getType()) {
  86.                     if (preg_match($regex$file->getFilename())) {
  87.                         return $file->isReadable();
  88.                     }
  89.                     return false;
  90.                 }
  91.                 return $file->isReadable();
  92.             });
  93.         }
  94.         $this->dispatch(FileManagerEvents::POST_FILE_FILTER_CONFIGURATION, ['finder' => $finderFiles]);
  95.         $formDelete $this->createDeleteForm()->createView();
  96.         $fileArray = [];
  97.         foreach ($finderFiles as $file) {
  98.             $fileArray[] = new File($file$this->translator$fileTypeService$fileManager);
  99.         }
  100.         if ('dimension' === $orderBy) {
  101.             usort($fileArray, function (File $aFile $b) {
  102.                 $aDimension $a->getDimension();
  103.                 $bDimension $b->getDimension();
  104.                 if ($aDimension && !$bDimension) {
  105.                     return 1;
  106.                 }
  107.                 if (!$aDimension && $bDimension) {
  108.                     return -1;
  109.                 }
  110.                 if (!$aDimension && !$bDimension) {
  111.                     return 0;
  112.                 }
  113.                 return ($aDimension[0] * $aDimension[1]) - ($bDimension[0] * $bDimension[1]);
  114.             });
  115.         }
  116.         if ($orderDESC) {
  117.             $fileArray array_reverse($fileArray);
  118.         }
  119.         $parameters = [
  120.             'fileManager' => $fileManager,
  121.             'fileArray' => $fileArray,
  122.             'formDelete' => $formDelete,
  123.         ];
  124.         if ($isJson) {
  125.             $fileList $this->renderView('@ArtgrisFileManager/views/_manager_view.html.twig'$parameters);
  126.             return new JsonResponse(['data' => $fileList'badge' => $finderFiles->count(), 'treeData' => $directoriesArbo]);
  127.         }
  128.         $parameters['treeData'] = json_encode($directoriesArbo);
  129.         $form $this->formFactory->createNamedBuilder('rename'FormType::class)
  130.             ->add('name'TextType::class, [
  131.                 'constraints' => [
  132.                     new NotBlank(),
  133.                 ],
  134.                 'label' => false,
  135.                 'data' => $this->translator->trans('input.default'),
  136.             ])
  137.             ->add('send'SubmitType::class, [
  138.                 'attr' => [
  139.                     'class' => 'btn btn-primary',
  140.                 ],
  141.                 'label' => $this->translator->trans('button.save'),
  142.             ])
  143.             ->getForm();
  144.         /* @var Form $form */
  145.         $form->handleRequest($request);
  146.         /** @var Form $formRename */
  147.         $formRename $this->createRenameForm();
  148.         if ($form->isSubmitted() && $form->isValid()) {
  149.             $data $form->getData();
  150.             $fs = new Filesystem();
  151.             $directory $directorytmp $fileManager->getCurrentPath().\DIRECTORY_SEPARATOR.$data['name'];
  152.             $i 1;
  153.             while ($fs->exists($directorytmp)) {
  154.                 $directorytmp "{$directory} ({$i})";
  155.                 ++$i;
  156.             }
  157.             $directory $directorytmp;
  158.             try {
  159.                 $fs->mkdir($directory);
  160.                 $this->addFlash('success'$this->translator->trans('folder.add.success'));
  161.             } catch (IOExceptionInterface $e) {
  162.                 $this->addFlash('danger'$this->translator->trans('folder.add.danger', ['%message%' => $data['name']]));
  163.             }
  164.             return $this->redirectToRoute('file_manager'$fileManager->getQueryParameters());
  165.         }
  166.         $parameters['form'] = $form->createView();
  167.         $parameters['formRename'] = $formRename->createView();
  168.         return $this->render('@ArtgrisFileManager/manager.html.twig'$parameters);
  169.     }
  170.     #[Route("/rename/{fileName}"name'file_manager_rename')]
  171.     public function renameFileAction(Request $requeststring $fileName): RedirectResponse {
  172.         $queryParameters $request->query->all();
  173.         $formRename $this->createRenameForm();
  174.         /* @var Form $formRename */
  175.         $formRename->handleRequest($request);
  176.         if ($formRename->isSubmitted() && $formRename->isValid()) {
  177.             $data $formRename->getData();
  178.             $extension $data['extension'] ? '.'.$data['extension'] : '';
  179.             $newfileName $data['name'].$extension;
  180.             if ($newfileName !== $fileName && isset($data['name'])) {
  181.                 $fileManager $this->newFileManager($queryParameters);
  182.                 $newfilePath $fileManager->getCurrentPath().\DIRECTORY_SEPARATOR.$newfileName;
  183.                 $oldfilePath realpath($fileManager->getCurrentPath().\DIRECTORY_SEPARATOR.$fileName);
  184.                 if (!== mb_strpos($newfilePath$fileManager->getCurrentPath())) {
  185.                     $this->addFlash('danger'$this->translator->trans('file.renamed.unauthorized'));
  186.                 } else {
  187.                     $fs = new Filesystem();
  188.                     try {
  189.                         $this->dispatch(FileManagerEvents::RENAME_FILE, ['oldFile'=> $oldfilePath,'newFile'=> $newfilePath]);
  190.                         $fs->rename($oldfilePath$newfilePath);
  191.                         $this->addFlash('success'$this->translator->trans('file.renamed.success'));
  192.                         //File has been renamed successfully
  193.                     } catch (IOException $exception) {
  194.                         $this->addFlash('danger'$this->translator->trans('file.renamed.danger'));
  195.                     } catch (Exception $exception) {
  196.                         $this->addFlash('danger'$exception->getMessage() );
  197.                     }
  198.                 }
  199.             } else {
  200.                 $this->addFlash('warning'$this->translator->trans('file.renamed.nochanged'));
  201.             }
  202.         }
  203.         return $this->redirectToRoute('file_manager'$queryParameters);
  204.     }
  205.     #[Route("/upload/"name'file_manager_upload')]
  206.     public function uploadFileAction(Request $request): JsonResponse|Response {
  207.         $fileManager $this->newFileManager($request->query->all());
  208.         $options = [
  209.             'upload_dir' => $fileManager->getCurrentPath().\DIRECTORY_SEPARATOR,
  210.             'upload_url' => implode('/'array_map('rawurlencode'explode('/'$fileManager->getImagePath()))),
  211.             'accept_file_types' => $fileManager->getRegex(),
  212.             'print_response' => false,
  213.             'override' => false,
  214.             'image_versions' => array(
  215.                 '' => array(
  216.                     'auto_orient' => true
  217.                 ),
  218.             ),
  219.         ];
  220.         if (isset($fileManager->getConfiguration()['upload'])) {
  221.             $options $fileManager->getConfiguration()['upload'] + $options;
  222.         }
  223.         $this->dispatch(FileManagerEvents::PRE_UPDATE, ['options' => &$options]);
  224.         $uploadHandler = new FileManagerUploadHandler($options);
  225.         $response $uploadHandler->get_response();
  226.         foreach ($response['files'] as $file) {
  227.             if (isset($file->error)) {
  228.                 $file->error $this->translator->trans($file->error);
  229.             } else {
  230.                 if (!$fileManager->getImagePath()) {
  231.                     $file->url $this->generateUrl('file_manager_file'array_merge($fileManager->getQueryParameters(), ['fileName' => $file->url]));
  232.                 }
  233.             }
  234.         }
  235.         $this->dispatch(FileManagerEvents::POST_UPDATE, ['response' => &$response]);
  236.         return new JsonResponse($response);
  237.     }
  238.     #[Route("/file/{fileName}"name'file_manager_file')]
  239.     public function binaryFileResponseAction(Request $requeststring $fileName): BinaryFileResponse {
  240.         $fileManager $this->newFileManager($request->query->all());
  241.         $file $fileManager->getCurrentPath().\DIRECTORY_SEPARATOR.urldecode($fileName);
  242.         $this->dispatch(FileManagerEvents::FILE_ACCESS, ['path' => $file]);
  243.         return new BinaryFileResponse($file);
  244.     }
  245.     #[Route("/delete/"name'file_manager_delete')]
  246.     public function deleteAction(Request $request): RedirectResponse {
  247.         $form $this->createDeleteForm();
  248.         $form->handleRequest($request);
  249.         $queryParameters $request->query->all();
  250.         if ($form->isSubmitted() && $form->isValid()) {
  251.             // remove file
  252.             $fileManager $this->newFileManager($queryParameters);
  253.             $fs = new Filesystem();
  254.             if (isset($queryParameters['delete'])) {
  255.                 $is_delete false;
  256.                 foreach ($queryParameters['delete'] as $fileName) {
  257.                     $filePath realpath($fileManager->getCurrentPath().\DIRECTORY_SEPARATOR.$fileName);
  258.                     if (!== mb_strpos($filePath$fileManager->getCurrentPath())) {
  259.                         $this->addFlash('danger''file.deleted.danger');
  260.                     } else {
  261.                          try {
  262.                             $this->dispatch(FileManagerEvents::PRE_DELETE_FILE);
  263.                             $fs->remove($filePath);
  264.                             $is_delete true;
  265.                             $this->dispatch(FileManagerEvents::POST_DELETE_FILE);
  266.                         } catch (IOException $exception) {
  267.                             $this->addFlash('danger''file.deleted.unauthorized');
  268.                         } catch (Exception $exception) {
  269.                             $this->addFlash('danger'$exception->getMessage() );
  270.                         }
  271.                     }
  272.                 }
  273.                 if ($is_delete) {
  274.                     $this->addFlash('success''file.deleted.success');
  275.                 }
  276.                 unset($queryParameters['delete']);
  277.             } else {
  278.                 
  279.                 try {
  280.                     $this->dispatch(FileManagerEvents::PRE_DELETE_FOLDER);
  281.                     $fs->remove($fileManager->getCurrentPath());
  282.                     $this->addFlash('success''folder.deleted.success');
  283.                 } catch (IOException $exception) {
  284.                     $this->addFlash('danger''folder.deleted.unauthorized');
  285.                 } catch (Exception $exception) {
  286.                     $this->addFlash('danger'$exception->getMessage() );
  287.                 }
  288.                 $this->dispatch(FileManagerEvents::POST_DELETE_FOLDER);
  289.                 $queryParameters['route'] = \dirname($fileManager->getCurrentRoute());
  290.                 if ($queryParameters['route'] == '/') {
  291.                     unset($queryParameters['route']);
  292.                 }
  293.                 return $this->redirectToRoute('file_manager'$queryParameters);
  294.             }
  295.         }
  296.         return $this->redirectToRoute('file_manager'$queryParameters);
  297.     }
  298.     private function createDeleteForm(): FormInterface|Form {
  299.         return $this->formFactory->createNamedBuilder('delete_f')
  300.             ->add('DELETE'SubmitType::class, [
  301.                 'translation_domain' => 'messages',
  302.                 'attr' => [
  303.                     'class' => 'btn btn-danger',
  304.                 ],
  305.                 'label' => 'button.delete.action',
  306.             ])
  307.             ->getForm();
  308.     }
  309.     private function createRenameForm(): FormInterface|Form {
  310.         return $this->formFactory->createNamedBuilder('rename_f')
  311.             ->add('name'TextType::class, [
  312.                 'constraints' => [
  313.                     new NotBlank(),
  314.                 ],
  315.                 'label' => false,
  316.             ])->add('extension'HiddenType::class)
  317.             ->add('send'SubmitType::class, [
  318.                 'attr' => [
  319.                     'class' => 'btn btn-primary',
  320.                 ],
  321.                 'label' => 'button.rename.action',
  322.             ])
  323.             ->getForm();
  324.     }
  325.     private function retrieveSubDirectories(FileManager $fileManagerstring $path, ?string $parent \DIRECTORY_SEPARATOR, ?string $baseFolderName null): ?array {
  326.         $directories = new Finder();
  327.         $directories->in($path)->ignoreUnreadableDirs()->directories()->depth(0)->sortByType()->filter(function (SplFileInfo $file) {
  328.             return $file->isReadable();
  329.         });
  330.         $this->dispatch(FileManagerEvents::POST_DIRECTORY_FILTER_CONFIGURATION, ['finder' => $directories]);
  331.         if ($baseFolderName) {
  332.             $directories->name($baseFolderName);
  333.         }
  334.         $directoriesList null;
  335.         foreach ($directories as $directory) {
  336.             /** @var SplFileInfo $directory */
  337.             $directoryFileName $directory->getFilename();
  338.             $fileName $baseFolderName '' $parent.$directoryFileName;
  339.             $queryParameters $fileManager->getQueryParameters();
  340.             $queryParameters['route'] = $fileName;
  341.             $queryParametersRoute $queryParameters;
  342.             unset($queryParametersRoute['route']);
  343.             $fileSpan '';
  344.             if (true === $fileManager->getConfiguration()['show_file_count']) {
  345.                 $filesNumber $this->retrieveFilesNumber($directory->getPathname(), $fileManager->getRegex());
  346.                 $fileSpan $filesNumber " <span class='label label-default'>{$filesNumber}</span>" '';
  347.             }
  348.             if ($fileName === '' && isset($fileManager->getConfiguration()['root_name'])) {
  349.                 $directoryFileName $fileManager->getConfiguration()['root_name'];
  350.             }
  351.             $directoriesList[] = [
  352.                 'text' => $directoryFileName.$fileSpan,
  353.                 'icon' => 'far fa-folder-open',
  354.                 'children' => $this->retrieveSubDirectories($fileManager$directory->getPathname(), $fileName.\DIRECTORY_SEPARATOR),
  355.                 'a_attr' => [
  356.                     'href' => $fileName $this->generateUrl('file_manager'$queryParameters) : $this->generateUrl('file_manager'$queryParametersRoute),
  357.                 ],
  358.                 'state' => [
  359.                     'selected' => $fileManager->getCurrentRoute() === $fileName,
  360.                     'opened' => true,
  361.                 ],
  362.             ];
  363.         }
  364.         return $directoriesList;
  365.     }
  366.     /**
  367.      * Tree Iterator.
  368.      */
  369.     private function retrieveFilesNumber(string $path,string $regex): int {
  370.         $files = new Finder();
  371.         $files->in($path)->files()->depth(0)->name($regex);
  372.         $this->dispatch(FileManagerEvents::POST_FILE_FILTER_CONFIGURATION, ['finder' => $files]);
  373.         return iterator_count($files);
  374.     }
  375.     private function newFileManager(array $queryParameters): FileManager {
  376.         if (!isset($queryParameters['conf'])) {
  377.             throw new \RuntimeException('Please define a conf parameter in your route');
  378.         }
  379.         $webDir $this->getParameter('artgris_file_manager')['web_dir'];
  380.         $this->fileManager = new FileManager($queryParameters$this->filemanagerService->getBasePath($queryParameters), $this->router$this->dispatcher$webDir);
  381.         return $this->fileManager;
  382.     }
  383.     protected function dispatch(string $eventName, array $arguments = []) {
  384.         $arguments array_replace([
  385.             'filemanager' => $this->fileManager,
  386.         ], $arguments);
  387.         $subject $arguments['filemanager'];
  388.         $event = new GenericEvent($subject$arguments);
  389.         $this->dispatcher->dispatch($event$eventName);
  390.     }
  391. }