Spaces:
No application file
No application file
| namespace Mautic\CoreBundle\Controller\Api; | |
| use Doctrine\Persistence\ManagerRegistry; | |
| use Mautic\ApiBundle\Controller\CommonApiController; | |
| use Mautic\ApiBundle\Helper\EntityResultHelper; | |
| use Mautic\CoreBundle\Factory\MauticFactory; | |
| use Mautic\CoreBundle\Factory\ModelFactory; | |
| use Mautic\CoreBundle\Helper\AppVersion; | |
| use Mautic\CoreBundle\Helper\CoreParametersHelper; | |
| use Mautic\CoreBundle\Helper\InputHelper; | |
| use Mautic\CoreBundle\Helper\PathsHelper; | |
| use Mautic\CoreBundle\Security\Permissions\CorePermissions; | |
| use Mautic\CoreBundle\Translation\Translator; | |
| use Psr\Log\LoggerInterface; | |
| use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
| use Symfony\Component\Form\FormFactoryInterface; | |
| use Symfony\Component\HttpFoundation\Request; | |
| use Symfony\Component\HttpFoundation\RequestStack; | |
| use Symfony\Component\HttpFoundation\Response; | |
| use Symfony\Component\Routing\RouterInterface; | |
| /** | |
| * @extends CommonApiController<object> | |
| */ | |
| class FileApiController extends CommonApiController | |
| { | |
| /** | |
| * Holds array of allowed file extensions. | |
| * | |
| * @var array | |
| */ | |
| protected $allowedExtensions = []; | |
| public function __construct(CorePermissions $security, Translator $translator, EntityResultHelper $entityResultHelper, RouterInterface $router, FormFactoryInterface $formFactory, AppVersion $appVersion, RequestStack $requestStack, ManagerRegistry $doctrine, ModelFactory $modelFactory, EventDispatcherInterface $dispatcher, CoreParametersHelper $coreParametersHelper, MauticFactory $factory) | |
| { | |
| $this->entityNameOne = 'file'; | |
| $this->entityNameMulti = 'files'; | |
| $this->allowedExtensions = $coreParametersHelper->get('allowed_extensions'); | |
| parent::__construct($security, $translator, $entityResultHelper, $router, $formFactory, $appVersion, $requestStack, $doctrine, $modelFactory, $dispatcher, $coreParametersHelper, $factory); | |
| } | |
| /** | |
| * Uploads a file. | |
| * | |
| * @return Response | |
| */ | |
| public function createAction(Request $request, PathsHelper $pathsHelper, LoggerInterface $mauticLogger, $dir) | |
| { | |
| try { | |
| $path = $this->getAbsolutePath($request, $pathsHelper, $mauticLogger, $dir, true); | |
| } catch (\Exception $e) { | |
| return $this->returnError($e->getMessage(), Response::HTTP_NOT_ACCEPTABLE); | |
| } | |
| $response = [$this->entityNameOne => []]; | |
| if ($request->files) { | |
| foreach ($request->files as $file) { | |
| $extension = $file->guessExtension() ?: $file->getClientOriginalExtension(); | |
| if (in_array($extension, $this->allowedExtensions)) { | |
| $fileName = md5(uniqid()).'.'.$extension; | |
| $moved = $file->move($path, $fileName); | |
| if (str_starts_with($dir, 'images')) { | |
| $response[$this->entityNameOne]['link'] = $this->getMediaUrl($request).'/'.$fileName; | |
| } | |
| $response[$this->entityNameOne]['name'] = $fileName; | |
| } else { | |
| return $this->returnError('The uploaded file can have only these extensions: '.implode(',', $this->allowedExtensions).'.', Response::HTTP_NOT_ACCEPTABLE); | |
| } | |
| } | |
| } else { | |
| return $this->returnError('File was not found in the request.', Response::HTTP_NOT_ACCEPTABLE); | |
| } | |
| $view = $this->view($response); | |
| return $this->handleView($view); | |
| } | |
| /** | |
| * List the files in /media directory. | |
| * | |
| * @return Response | |
| */ | |
| public function listAction(Request $request, PathsHelper $pathsHelper, LoggerInterface $mauticLogger, $dir) | |
| { | |
| try { | |
| $filePath = $this->getAbsolutePath($request, $pathsHelper, $mauticLogger, $dir); | |
| } catch (\Exception $e) { | |
| return $this->returnError($e->getMessage(), Response::HTTP_NOT_ACCEPTABLE); | |
| } | |
| $fnames = scandir($filePath); | |
| if (is_array($fnames)) { | |
| foreach ($fnames as $key => $name) { | |
| // remove hidden files | |
| if (str_starts_with($name, '.')) { | |
| unset($fnames[$key]); | |
| } | |
| } | |
| } else { | |
| return $this->returnError(ucfirst($dir).' dir is not readable'); | |
| } | |
| $view = $this->view([$this->entityNameMulti => $fnames]); | |
| return $this->handleView($view); | |
| } | |
| /** | |
| * Delete a file from /media directory. | |
| * | |
| * @return Response | |
| */ | |
| public function deleteAction(Request $request, PathsHelper $pathsHelper, LoggerInterface $mauticLogger, $dir, $file) | |
| { | |
| $response = ['success' => false]; | |
| try { | |
| $filePath = $this->getAbsolutePath($request, $pathsHelper, $mauticLogger, $dir).'/'.basename($file); | |
| } catch (\Exception $e) { | |
| return $this->returnError($e->getMessage(), Response::HTTP_NOT_ACCEPTABLE); | |
| } | |
| if (!file_exists($filePath)) { | |
| return $this->returnError('File does not exist', Response::HTTP_NOT_FOUND); | |
| } elseif (!is_writable($filePath)) { | |
| return $this->returnError('File is not writable'); | |
| } else { | |
| unlink($filePath); | |
| $response['success'] = true; | |
| } | |
| $view = $this->view($response); | |
| return $this->handleView($view); | |
| } | |
| /** | |
| * Get the Media directory full file system path. | |
| * | |
| * @param string $dir | |
| * @param bool $createDir | |
| * | |
| * @return string | |
| */ | |
| protected function getAbsolutePath(Request $request, PathsHelper $pathsHelper, LoggerInterface $mauticLogger, $dir, $createDir = false) | |
| { | |
| try { | |
| $possibleDirs = ['media', 'images']; | |
| $dir = InputHelper::alphanum($dir, true, null, ['_', '.']); | |
| $subdir = trim(InputHelper::alphanum($request->get('subdir', ''), true, null, ['/'])); | |
| // Dots in the dir name are slashes | |
| if (str_contains($dir, '.') && !$subdir) { | |
| $dirs = explode('.', $dir); | |
| $dir = $dirs[0]; | |
| unset($dirs[0]); | |
| $subdir = implode('/', $dirs); | |
| } | |
| if (!in_array($dir, $possibleDirs)) { | |
| throw new \InvalidArgumentException($dir.' not found. Only '.implode(' or ', $possibleDirs).' options are possible.'); | |
| } | |
| if ('images' === $dir) { | |
| $absoluteDir = realpath($pathsHelper->getSystemPath($dir, true)); | |
| } elseif ('media' === $dir) { | |
| $absoluteDir = realpath($this->coreParametersHelper->get('upload_dir')); | |
| } | |
| if (false === $absoluteDir) { | |
| throw new \InvalidArgumentException($dir.' dir does not exist', Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| if (false === is_writable($absoluteDir)) { | |
| throw new \InvalidArgumentException($dir.' dir is not writable', Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| $path = $absoluteDir.'/'.$subdir; | |
| if (!file_exists($path)) { | |
| if ($createDir) { | |
| if (false === mkdir($path)) { | |
| throw new \InvalidArgumentException($dir.'/'.$subdir.' subdirectory could not be created.', Response::HTTP_INTERNAL_SERVER_ERROR); | |
| } | |
| } else { | |
| throw new \InvalidArgumentException($subdir.' doesn\'t exist in the '.$dir.' dir.'); | |
| } | |
| } | |
| return $path; | |
| } catch (\Exception $e) { | |
| $mauticLogger->error($e->getMessage(), ['exception' => $e]); | |
| throw $e; | |
| } | |
| } | |
| /** | |
| * Get the Media directory full file system path. | |
| */ | |
| protected function getMediaUrl(Request $request): string | |
| { | |
| return $request->getScheme().'://' | |
| .$request->getHttpHost() | |
| .':'.$request->getPort() | |
| .$request->getBasePath().'/' | |
| .$this->coreParametersHelper->get('image_path'); | |
| } | |
| } | |