src/Controller/UploadController.php line 230

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Constant\UtilsConstant;
  4. use App\Entity\BandMember;
  5. use App\Entity\Project;
  6. use App\Entity\User;
  7. use App\Exception\BadRequestException;
  8. use App\Services\StorageManager;
  9. use Symfony\Component\HttpFoundation\File\UploadedFile;
  10. use FOS\RestBundle\Controller\Annotations as Rest;
  11. use Symfony\Component\HttpFoundation\Request;
  12. use Symfony\Component\HttpFoundation\Response;
  13. use Symfony\Component\HttpFoundation\JsonResponse;
  14. use App\Constant\FileActionConstant;
  15. use App\Constant\FileTypeConstant;
  16. use App\Entity\File;
  17. use App\Entity\Folder;
  18. use App\Entity\Playable;
  19. use App\Entity\Picture;
  20. use Symfony\Component\Mercure\Update;
  21. use Symfony\Component\Mercure\HubInterface;
  22. use Symfony\Contracts\Translation\TranslatorInterface;
  23. const PROFILE 'profile';
  24. const COVER 'cover';
  25. const PICTURE 'picture';
  26. const CLASS_KEY 'class';
  27. const CLASSNAME 'className';
  28. const MESSAGE 'message';
  29. class UploadController extends WhiplayController
  30. {
  31.     const EDITABLE_PICTURES = [
  32.         'userProfile' => [
  33.             'type' => PROFILE,
  34.             PICTURE => null,
  35.             'path' => 'users/',
  36.             CLASS_KEY => 'User',
  37.             CLASSNAME => 'user-profile-picture-img'
  38.         ],
  39.         'userCover' => [
  40.             'type' => COVER,
  41.             PICTURE => null,
  42.             'path' => 'users/',
  43.             CLASS_KEY => 'User',
  44.             CLASSNAME => 'user-cover-picture-img'
  45.         ],
  46.         'bandProfile' => [
  47.             'type' => PROFILE,
  48.             PICTURE => null,
  49.             'path' => 'bands/',
  50.             CLASS_KEY => 'Band',
  51.             CLASSNAME => 'band-profile-picture-img'
  52.         ],
  53.         'bandCover' => [
  54.             'type' => COVER,
  55.             PICTURE => null,
  56.             'path' => 'bands/',
  57.             CLASS_KEY => 'Band',
  58.             CLASSNAME => 'band-cover-picture-img'
  59.         ],
  60.         'file' => [
  61.             'type' => 'image',
  62.             PICTURE => null,
  63.             'path' => 'files/',
  64.             CLASS_KEY => 'File',
  65.             CLASSNAME => 'file-picture-img'
  66.         ],
  67.     ];
  68.     const ALLOWED_MUSIC_TYPES = [
  69.         'audio/mpeg' => 'mp3',
  70.         'application/octet-stream' => 'mp3',
  71.         'audio/x-wav' => 'wav',
  72.         'audio/wav' => 'wav',
  73.     ];
  74.     const ALLOWED_PICTURE_TYPES = [
  75.         'image/jpeg' => 'jpg',
  76.         'image/jpg' => 'jpg',
  77.         'image/png' => 'png',
  78.     ];
  79.     /**
  80.      * Upload a picture to replace one of:
  81.      * - The band's cover or profile picture
  82.      * - The user's cover or profile picture
  83.      * - The band's cover or profile picture
  84.      *
  85.      * @Rest\View()
  86.      * @Rest\Post("/upload/picture")
  87.      *
  88.      * @param Request $request
  89.      * @param TranslatorInterface $translator
  90.      * @return JsonResponse
  91.      */
  92.     public function picture(Request $requestTranslatorInterface $translator): JsonResponse
  93.     {
  94.         try {
  95.             // Check if current request come from AJAX
  96.             if (!$request->isXmlHttpRequest()) {
  97.                 throw new BadRequestException('upload.forbidden');
  98.             }
  99.             // Check if user is logged
  100.             $user $this->getUser();
  101.             if (!($user instanceof User)) {
  102.                 throw new BadRequestException('upload.need_login');
  103.             }
  104.             // check if data64 and name get param are valid
  105.             $base64Content $request->get('data64');
  106.             $pictureName $request->get('name');
  107.             $entityId $request->get('entityId');
  108.             if (!$base64Content || !isset(self::EDITABLE_PICTURES[$pictureName]) || !$entityId) {
  109.                 throw new BadRequestException('upload.invalid_parameters');
  110.             }
  111.             $data self::EDITABLE_PICTURES[$pictureName];
  112.             // Check if entity class is found
  113.             $entityClass $data[CLASS_KEY];
  114.             if (!$entityClass) {
  115.                 throw new BadRequestException('upload.picture.class_not_found');
  116.             }
  117.             // Check if entity is found
  118.             $entity $this->getDoctrine()
  119.                 ->getRepository('App:' $entityClass)
  120.                 ->find($entityId);
  121.             if (!isset($entity)) {
  122.                 throw new BadRequestException('upload.picture.entity_not_found');
  123.             }
  124.             if ($entityClass === 'User' && $user->getId() !== $entity->getId()) {
  125.                 throw new BadRequestException('upload.picture.user_upload_as_external');
  126.             }
  127.             if ($entityClass === 'Band' && !$user->isMemberButNotGuest($entity)) {
  128.                 throw new BadRequestException('upload.picture.upload_as_guest');
  129.             }
  130.             if ($entityClass === 'File' && !$user->isMemberButNotGuest($entity->getFolder()->getOwner())) {
  131.                 throw new BadRequestException('upload.picture.upload_as_guest');
  132.             }
  133.             // Check if extension is valid
  134.             $extension $this->extensionIsValid($base64Content);
  135.             if (!$extension) {
  136.                 throw new BadRequestException('upload.picture.invalid_format');
  137.             }
  138.         } catch (BadRequestException $exception) {
  139.             return new JsonResponse([MESSAGE => $translator->trans($exception->getMessage())], Response::HTTP_BAD_REQUEST);
  140.         }
  141.         $picture = new Picture();
  142.         $picture->setBase64Content($base64Content);
  143.         $picture->setExtension($extension);
  144.         $picture->setSubPath($data['path']);
  145.         $em $this->getDoctrine()->getManager();
  146.         $em->persist($picture);
  147.         $postData = [];
  148.         $postData['type'] = $data['type'];
  149.         $postData[CLASSNAME] = $data[CLASSNAME];
  150.         if (self::EDITABLE_PICTURES[$pictureName]['type'] === COVER) {
  151.             $oldPicture $entity->getCommonInformation()->getCoverPicture();
  152.             $entity->getCommonInformation()->setCoverPicture($picture);
  153.         } else if (self::EDITABLE_PICTURES[$pictureName]['type'] === PROFILE) {
  154.             $oldPicture $entity->getCommonInformation()->getProfilePicture();
  155.             $entity->getCommonInformation()->setProfilePicture($picture);
  156.         } else {
  157.             $oldPicture $entity->getPicture();
  158.             $entity->setPicture($picture);
  159.         }
  160.         if ($oldPicture instanceof Picture) {
  161.             $em->remove($oldPicture);
  162.         }
  163.         $em->persist($entity);
  164.         $em->flush();
  165.         $postData['src'] = $this->getParameter(UtilsConstant::GLOBAL_URL) . $picture->getWebPath();
  166.         return new JsonResponse($postData);
  167.     }
  168.     /**
  169.      * Check if extension is valid from base64 content
  170.      * return extension or null if it's invalid
  171.      *
  172.      * @param string $content
  173.      * @return string|null
  174.      */
  175.     private function extensionIsValid(string $content): ?string
  176.     {
  177.         $validExtension false;
  178.         $fileType substr($content5strrpos($content';') - 5);
  179.         foreach (array_keys(self::ALLOWED_PICTURE_TYPES) as $currentExt) {
  180.             $end strtolower(substr($fileType, -strlen($currentExt)));
  181.             if ($end === $currentExt) {
  182.                 $validExtension true;
  183.                 break;
  184.             }
  185.         }
  186.         $explodeExtension explode('/'$fileType);
  187.         if ($validExtension && count($explodeExtension) === 2) {
  188.             return $explodeExtension[1];
  189.         }
  190.         return null;
  191.     }
  192.     /**
  193.      * Upload a file in $folderId in user file repository
  194.      *
  195.      * @Rest\View()
  196.      * @Rest\Post("/upload/file")
  197.      *
  198.      * @param Request $request
  199.      * @param StorageManager $storageManager
  200.      * @param TranslatorInterface $translator
  201.      * @param HubInterface $hub
  202.      * @return JsonResponse
  203.      */
  204.     public function uploadFile(
  205.         Request             $request,
  206.         StorageManager      $storageManager,
  207.         TranslatorInterface $translator,
  208.         HubInterface        $hub
  209.     ): JsonResponse
  210.     {
  211.         try {
  212.             if (!$request->isXmlHttpRequest()) {
  213.                 throw new BadRequestException('upload.forbidden');
  214.             }
  215.             /** @var User $user */
  216.             $user $this->getUser();
  217.             if (!($user instanceof User)) {
  218.                 throw new BadRequestException('upload.need_login');
  219.             }
  220.             $folderId $request->get('folderId');
  221.             if (!$folderId) {
  222.                 throw new BadRequestException('upload.invalid_parameters');
  223.             }
  224.             /** @var Folder $folder */
  225.             $folder $this->getDoctrine()
  226.                 ->getRepository(Folder::class)
  227.                 ->find($folderId);
  228.             if (!$folder) {
  229.                 throw new BadRequestException('upload.file.folder_not_found');
  230.             }
  231.             $band $folder->getOwner();
  232.             if (!$user->isInBand($band)) {
  233.                 throw new BadRequestException('upload.file.invalid_band');
  234.             }
  235.             if (!$user->isMemberButNotGuest($band)) {
  236.                 throw new BadRequestException('upload.file.upload_as_guest');
  237.             }
  238.             $fileInputName 'add-file';
  239.             /* @var UploadedFile $file */
  240.             $file $request->files->get($fileInputName);
  241.             if ($file->getError() > 0) {
  242.                 throw new BadRequestException('upload.file.failed');
  243.             }
  244.             $fullUsedSize $storageManager->getStorageUsage($band);
  245.             $fullAvailableSize $storageManager->getAvailableStorage($band);
  246.             $remainingSize $fullAvailableSize - ($fullUsedSize + ($file->getSize() / (1024 1024)));
  247.             if ($remainingSize 0) {
  248.                 throw new BadRequestException('upload.file.too_big');
  249.             }
  250.         } catch (BadRequestException $exception) {
  251.             return new JsonResponse([MESSAGE => $translator->trans($exception->getMessage())]);
  252.         }
  253.         $em $this->getDoctrine()->getManager();
  254.         $postData = [];
  255.         $newFile = new File();
  256.         $newFile->setName(pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME));
  257.         $newFile->setFile($file);
  258.         $newFileType $file->getMimeType();
  259.         if (array_key_exists($newFileTypeself::ALLOWED_MUSIC_TYPES)) {
  260.             $newFile->setType(FileTypeConstant:: MUSIC);
  261.             $postData['fileType'] = FileTypeConstant::MUSIC;
  262.             $playable = new Playable();
  263.             $em->persist($playable);
  264.             $newFile->setPlayable($playable);
  265.         } elseif (array_key_exists($newFileTypeself::ALLOWED_PICTURE_TYPES)) {
  266.             $newFile->setType(FileTypeConstant::PICTURE);
  267.             $postData['fileType'] = FileTypeConstant::PICTURE;
  268.         } else {
  269.             return new JsonResponse([MESSAGE => $translator->trans('upload.file.invalid_type')], Response::HTTP_BAD_REQUEST);
  270.         }
  271.         if ($folder->getFilesType() == $newFile->getType()) {
  272.             $newFile->setFolder($folder);
  273.         } else {
  274.             $newFile->setFolder($folder->getOwner()->getBaseFolder($newFile->getType()));
  275.         }
  276.         $projectId $request->get('projectId');
  277.         if ($projectId) {
  278.             /** @var Project|null $project */
  279.             $project $this->getDoctrine()
  280.                 ->getRepository(Project::class)
  281.                 ->find($projectId);
  282.             if ($project && $project->getBand() === $band) {
  283.                 $newFile->setProject($project);
  284.             }
  285.         }
  286.         $em->persist($newFile);
  287.         $em->flush();
  288.         $this->toggleFileAction($newFileFileActionConstant::ADDfalse);
  289.         /** @var BandMember $bandMember */
  290.         foreach ($band->getMembers() as $bandMember) {
  291.             $update = new Update(
  292.                 'messaging/' $bandMember->getUser()->getId(),
  293.                 json_encode([
  294.                     'type' => 'new-file',
  295.                     'file' => [
  296.                         'id' => $newFile->getId(),
  297.                         'name' => $newFile->getName(),
  298.                         'type' => $newFile->getType(),
  299.                         'status' => $newFile->getStatus(),
  300.                         'show_route' => $this->generateUrl('whiplay_file_show', [
  301.                             'id' => $newFile->getId(),
  302.                         ]),
  303.                         'download_route' => $this->generateUrl('whiplay_download_music', [
  304.                             'id' => $newFile->getId(),
  305.                         ]),
  306.                     ]
  307.                 ])
  308.             );
  309.             $hub->publish($update);
  310.         }
  311.         $postData['url'] = $this->get('router')->generate('whiplay_file_edit', array('id' => $newFile->getId()));
  312.         return new JsonResponse($postData);
  313.     }
  314. }