src/Cms/FileBundle/Controller/ModalController.php line 712

Open in your IDE?
  1. <?php
  2. namespace Cms\FileBundle\Controller;
  3. use Cms\ContainerBundle\Entity\Container;
  4. use Cms\ContainerBundle\Entity\Containers\GenericContainer;
  5. use Cms\ContainerBundle\Entity\Containers\IntranetContainer;
  6. use Cms\ContainerBundle\Entity\Containers\PersonalContainer;
  7. use Cms\ContainerBundle\Entity\Containers\StorageContainer;
  8. use Cms\CoreBundle\Model\Scenes\DashboardScenes\AjaxScene;
  9. use Cms\CoreBundle\Model\Url;
  10. use Cms\CoreBundle\Service\Aws\S3Wrapper;
  11. use Cms\CoreBundle\Util\ModalsController;
  12. use Cms\DomainBundle\Entity\Domain;
  13. use Cms\FileBundle\Doctrine\Nodes\FileRepository;
  14. use Cms\FileBundle\Entity\Nodes\File;
  15. use Cms\FileBundle\Entity\Nodes\Folder;
  16. use Cms\FileBundle\Entity\Optimizations\ImageOptimization;
  17. use Cms\FileBundle\Form\Type\FolderType;
  18. use Cms\FileBundle\Service\FileManager;
  19. use Doctrine\Common\Util\ClassUtils;
  20. use Symfony\Component\Routing\Annotation\Route;
  21. use Symfony\Component\Form\Extension\Core\Type\TextType;
  22. use Symfony\Component\HttpFoundation\JsonResponse;
  23. use Symfony\Component\HttpFoundation\RedirectResponse;
  24. use Symfony\Component\HttpFoundation\Request;
  25. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  26. use Laminas\Uri\Uri;
  27. /**
  28.  * Class ModalController
  29.  * @package Cms\FileBundle\Controller
  30.  */
  31. final class ModalController extends ModalsController
  32. {
  33.     const ROUTES__THUMB 'campussuite.cms.file.modal.thumb';
  34.     const ROUTES__START 'campussuite.cms.file.modal.start';
  35.     const ROUTES__CONTAINER 'campussuite.cms.file.modal.container';
  36.     const ROUTES__CONTAINER_SORT 'campussuite.cms.file.modal.container_sort';
  37.     const ROUTES__FOLDER 'campussuite.cms.file.modal.folder';
  38.     const ROUTES__FOLDER_SORT 'campussuite.cms.file.modal.folder_sort';
  39.     const ROUTES__FOLDER_ROOT_CREATE 'campussuite.cms.file.modal.folder_root_create';
  40.     const ROUTES__FOLDER_CHILD_CREATE 'campussuite.cms.file.modal.folder_child_create';
  41.     const ROUTES__UPLOAD 'campussuite.cms.file.modal.upload';
  42.     const ROUTES__URL 'campussuite.cms.file.modal.url';
  43.     const ROUTES__FOLDER_FILES 'campussuite.cms.file.modal.folder_files';
  44.     const SESSION__LAST_LOCATION 'campussuite.cms.file.modal.last_location';
  45.     const SESSION__LAST_LOCATION_TYPE 'campussuite.cms.file.modal.last_location_type';
  46.     const SESSION__SORT 'campussuite.cms.file.modal.sort';
  47.     const MODES = array(
  48.         self::MODES__ID,
  49.         self::MODES__URL,
  50.         self::MODES__MIXED,
  51.         self::MODES__FOLDER,
  52.         self::MODES__ABSOLUTE,
  53.         self::MODES__SMM,
  54.         self::MODES__ATTACHMENTS,
  55.     );
  56.     const MODES__ID 'id';
  57.     const MODES__URL 'url';
  58.     const MODES__MIXED 'mixed';
  59.     const MODES__FOLDER 'folder';
  60.     const MODES__ABSOLUTE 'absolute';
  61.     const MODES__SMM 'smm';
  62.     const MODES__ATTACHMENTS 'attachments';
  63.     const PARAMS = array(
  64.         self::PARAMS__MODE,
  65.         self::PARAMS__TYPE,
  66.         self::PARAMS__CONTAINER,
  67.         self::PARAMS__VALUE,
  68.         self::PARAMS__SORT,
  69.     );
  70.     const PARAMS__MODE 'mode';
  71.     const PARAMS__TYPE 'type';
  72.     const PARAMS__CONTAINER 'container';
  73.     const PARAMS__VALUE 'value';
  74.     const PARAMS__SORT 'sort';
  75.     const DEFAULTS__PARAMS = array(
  76.         self::PARAMS__MODE => null,
  77.         self::PARAMS__TYPE => null,
  78.         self::PARAMS__CONTAINER => null,
  79.         self::PARAMS__VALUE => null,
  80.         self::PARAMS__SORT => null,
  81.     );
  82.     /**
  83.      * @param string $folderId
  84.      * @return JsonResponse
  85.      * @throws \Exception
  86.      *
  87.      * @Route(
  88.      *  "/folder-files/{folderId}",
  89.      *  name = ModalController::ROUTES__FOLDER_FILES
  90.      * )
  91.      */
  92.     public function folderFilesAction($folderId)
  93.     {
  94.         $folder $this->getEntityManager()->getRepository(Folder::class)->findExact($folderId);
  95.         $files $this->getEntityManager()->getRepository(File::class)->findBy(
  96.             array(
  97.                 'parent' => $folder->getId(),
  98.             ),
  99.             array(
  100.                 'name' => 'ASC',
  101.                 'extension' => 'ASC',
  102.             )
  103.         );
  104.         $result = [];
  105.         $path '';
  106.         $fold $folder;
  107.         do {
  108.             $path '/' $fold->getName() . $path;
  109.             $fold $fold->getParent();
  110.         } while ( ! empty($fold));
  111.         foreach ($files as $file) {
  112.             $result[] = array(
  113.                 'id' => $file->getId(),
  114.                 'name' => $file->getName(),
  115.                 'url' => (new Url(
  116.                     null,
  117.                     null,
  118.                     null,
  119.                     null,
  120.                     null,
  121.                     ('/files' $path '/' $file->getFilename()),
  122.                     null,
  123.                     null
  124.                 ))
  125.                     ->setCurrentContainer($folder->getContainer())
  126.                     ->compile(Url::FLAGS__FORCE_ROOT)
  127.             );
  128.         }
  129.         return new JsonResponse($result);
  130.     }
  131.     /**
  132.      * @param Request $request
  133.      * @return RedirectResponse
  134.      * @throws \Exception
  135.      *
  136.      * @Route(
  137.      *  "/thumb",
  138.      *  name = ModalController::ROUTES__THUMB
  139.      * )
  140.      */
  141.     public function thumbAction(Request $request)
  142.     {
  143.         // get the query string pieces
  144.         $value $request->query->get('value');
  145.         $host $request->query->get('host');
  146.         $size $request->query->get('size');
  147.         // value could be percent encoded, decode it
  148.         if (strpos($value'%') !== false) {
  149.             $value rawurldecode($value);
  150.         }
  151.         // handle cases when there is no host
  152.         if (empty($host)) {
  153.             $host $this->getEntityManager()->getRepository(GenericContainer::class)->findBy(
  154.                 array(
  155.                     'parent' => null,
  156.                 ),
  157.                 array(
  158.                     'id' => 'ASC',
  159.                 )
  160.             )[0];
  161.             $host sprintf(
  162.                 '%s.%s.campussuite.site',
  163.                 $host->getSlug(),
  164.                 $host->getTenant()->getSlug()
  165.             );
  166.         }
  167.         // assemble the url we will be redirecting to
  168.         $url null;
  169.         switch (true) {
  170.             // id; find the file and use file url service to get it
  171.             case preg_match('/^([1-9]\\d*)$/'$value$matches) === 1:
  172.                 if (empty($size) || ! array_key_exists($sizeImageOptimization::$sizes)) {
  173.                     $size ImageOptimization::MASKS__SQUARE__MEDIUM;
  174.                 }
  175.                 $file $this->getEntityManager()->getRepository(File::class)->findExact($value);
  176.                 $url $this->getS3Wrapper()->entityUrl(
  177.                     S3Wrapper::BUCKETS__STORAGE,
  178.                     $file,
  179.                     '/optimizations/' $size
  180.                 );
  181.                 break;
  182.             // absolute url
  183.             case preg_match('/^https?:\\/\\/([^\\/]+)/'$value$matches) === 1:
  184.                 // see if we host the domain
  185.                 $domain $this->getEntityManager()->getRepository(Domain::class)->findOneByHost($matches[1]);
  186.                 if ( ! empty($domain)) {
  187.                     // we do host, so we can assume that we can add a mask to the query string
  188.                     $purl = new Uri($value);
  189.                     $purl->setQuery(array_merge(
  190.                         $purl->getQueryAsArray(),
  191.                         array(
  192.                             'size' => null,
  193.                             'mask' => ImageOptimization::MASKS__SQUARE__MEDIUM,
  194.                             'const' => null,
  195.                         )
  196.                     ));
  197.                     $url $purl->toString();
  198.                 } else {
  199.                     $url $value;
  200.                 }
  201.                 break;
  202.             // file url
  203.             case preg_match('/^(.*?)\\/files\\/(.+)/'$value$matches) === 1:
  204.                 $containers array_values(array_filter(explode('/'$matches[1])));
  205.                 // handle intranet requests
  206.                 switch (true) {
  207.                     case (count($containers) > && strpos($containers[0], '~') === 0):
  208.                         $site $this->getEntityManager()->getRepository(IntranetContainer::class)->findOneBy(array(
  209.                             'parent' => null,
  210.                             'slug' => ltrim($containers[0], '~'),
  211.                         ));
  212.                         $containers array_slice($containers1);
  213.                         break;
  214.                     default:
  215.                         $fixedhost preg_replace('/^https?:\\/\\/([^\\/]+).*$/''$1'$host);
  216.                         if (preg_match('/^([-a-zA-Z0-9]+)\\.([-a-zA-Z0-9]+)\\.campussuite\\.site$/'$fixedhost$hostmatches) === 1) {
  217.                             $site $this->getEntityManager()->getRepository(GenericContainer::class)->findOneBy(array(
  218.                                 'parent' => null,
  219.                                 'slug' => $hostmatches[1],
  220.                             ));
  221.                         } else if (preg_match('/^[0-9]+$/'$fixedhost) === 1) {
  222.                             $site $this->getEntityManager()->getRepository(Container::class)->findExact($fixedhost);
  223.                             $site array_merge(
  224.                                 $this->getEntityManager()->getRepository(Container::class)->findAncestors($site),
  225.                                 array($site)
  226.                             )[0];
  227.                         } else {
  228.                             $domain $this->getEntityManager()->getRepository(Domain::class)->findOneByHost(
  229.                                 $fixedhost
  230.                             );
  231.                             $site $this->getEntityManager()->getRepository(GenericContainer::class)->findOneByDomain(
  232.                                 $domain
  233.                             );
  234.                         }
  235.                 }
  236.                 $container $site;
  237.                 $slugs = [];
  238.                 foreach ($containers as $slug) {
  239.                     $slugs[] = urldecode($slug);
  240.                     $container $this->getEntityManager()->getRepository(Container::class)->findOneBy(array(
  241.                         'parent' => $container,
  242.                         'slug' => urldecode($slug),
  243.                     ));
  244.                     if (empty($container)) {
  245.                         throw new NotFoundHttpException(sprintf(
  246.                             'Could not find container for path: /%s',
  247.                             implode('/'$slugs)
  248.                         ));
  249.                     }
  250.                 }
  251.                 $folders array_values(array_filter(explode('/'$matches[2])));
  252.                 $fileparts = [];
  253.                 $possibleFile array_pop($folders);
  254.                 if (preg_match('/(.*)\\.([a-zA-Z0-9]+)([?].*)?$/'$possibleFile$fileparts) !== 1) {
  255.                     throw new NotFoundHttpException(sprintf(
  256.                         'Possible file lookup does not match file regex: %s',
  257.                         $possibleFile
  258.                     ));
  259.                 }
  260.                 $folder null;
  261.                 $names = [];
  262.                 foreach ($folders as $name) {
  263.                     $names[] = urldecode($name);
  264.                     $folder $this->getEntityManager()->getRepository(Folder::class)->findOneBy(array(
  265.                         'container' => $container,
  266.                         'parent' => $folder,
  267.                         'name' => urldecode($name),
  268.                     ));
  269.                     if (empty($folder)) {
  270.                         throw new NotFoundHttpException(sprintf(
  271.                             'Could not find folder for path: /%s',
  272.                             implode('/'$names)
  273.                         ));
  274.                     }
  275.                 }
  276.                 $file $this->getEntityManager()->getRepository(File::class)->findOneBy(array(
  277.                     'parent' => $folder,
  278.                     'name' => urldecode($fileparts[1]),
  279.                     'extension' => urldecode($fileparts[2]),
  280.                 ));
  281.                 if (empty($file)) {
  282.                     throw new NotFoundHttpException(sprintf(
  283.                         'Could not find file: %s.%s',
  284.                         urldecode($fileparts[1]),
  285.                         urldecode($fileparts[2])
  286.                     ));
  287.                 }
  288.                 $qs = [];
  289.                 if (isset($fileparts[3])) {
  290.                     parse_str(ltrim($fileparts[3], '?'), $qs);
  291.                 }
  292.                 if ($size === '-1' && array_key_exists('mask'$qs)) {
  293.                     $size intval($qs['mask']);
  294.                 } else if ($size === '-1') {
  295.                     $size ImageOptimization::MASKS__GENERIC__FULL;
  296.                 }
  297.                 if (empty($size) || ! array_key_exists($sizeImageOptimization::$sizes)) {
  298.                     $size ImageOptimization::MASKS__SQUARE__MEDIUM;
  299.                 }
  300.                 $url $this->getS3Wrapper()->entityUrl(
  301.                     S3Wrapper::BUCKETS__STORAGE,
  302.                     $file,
  303.                     '/optimizations/' $size
  304.                 );
  305.                 break;
  306.             // ident
  307.             case preg_match('/^([1-9]\\d*):([^:]+):([1-9]\\d*):([^:]+)(?:[:](.+))?$/'$value$matches) === 1:
  308.                 if (empty($size) || ! array_key_exists($sizeImageOptimization::$sizes)) {
  309.                     $size ImageOptimization::MASKS__SQUARE__MEDIUM;
  310.                 }
  311.                 $url $this->getFileManager()->identUrlOverride(
  312.                     $value,
  313.                     array(
  314.                         'extra' => 'optimizations/' $size,
  315.                     )
  316.                 );
  317.                 break;
  318.         }
  319.         // check for result
  320.         if (empty($url)) {
  321.             throw new NotFoundHttpException();
  322.         }
  323.         // send back a redirect to the location of the preview
  324.         return new RedirectResponse($url302, array(
  325.             'Cache-Control' => 'public, max-age=3600',
  326.         ));
  327.     }
  328.     /**
  329.      * @return AjaxScene|RedirectResponse
  330.      *
  331.      * @Route(
  332.      *  "/",
  333.      *  name = ModalController::ROUTES__START
  334.      * )
  335.      */
  336.     public function startAction()
  337.     {
  338.         // get params
  339.         $last null;
  340.         // attempt to get container
  341.         $container null;
  342.         if ( ! empty($this->getModalParam(self::PARAMS__CONTAINER))) {
  343.             $container $this->getEntityManager()->getRepository(Container::class)->find(
  344.                 $this->getModalParam(self::PARAMS__CONTAINER)
  345.             );
  346.         }
  347.         // attempt to determine if there is a last location thing set
  348.         $lastId $this->getSession()->get(self::SESSION__LAST_LOCATION);
  349.         if ( ! empty($lastId)) {
  350.             $last $this->getEntityManager()
  351.                 ->getRepository($this->getSession()->get(self::SESSION__LAST_LOCATION_TYPE))
  352.                 ->findExact($lastId);
  353.         }
  354.         // if we are in id mode, the value might be an id we can latch onto for a file, and we can get a folder from it
  355.         if (
  356.                 $this->getModalParam(self::PARAMS__MODE) === self::MODES__FOLDER
  357.             &&
  358.                 preg_match('/^[1-9]\d*$/'$this->getModalParam(self::PARAMS__VALUE)) === 1
  359.         ) {
  360.             $folder $this->getEntityManager()->getRepository(Folder::class)->find(
  361.                 $this->getModalParam(self::PARAMS__VALUE)
  362.             );
  363.             if ( ! empty($folder)) {
  364.                 $last $folder;
  365.             }
  366.         }
  367.         else if (
  368.                 $this->getModalParam(self::PARAMS__MODE) !== self::MODES__URL
  369.             &&
  370.                 preg_match('/^[1-9]\d*$/'$this->getModalParam(self::PARAMS__VALUE)) === 1
  371.         ) {
  372.             $file $this->getEntityManager()->getRepository(File::class)->find(
  373.                 $this->getModalParam(self::PARAMS__VALUE)
  374.             );
  375.             if ( ! empty($file)) {
  376.                 $last $file->getParent();
  377.             }
  378.         }
  379.         // generate redirect based on information
  380.         if ($last instanceof Folder && ! empty($container) && $this->compareLastToCurrent($last->getContainer(), $container)) {
  381.             // redirect to that folder's view
  382.             return $this->redirectToRoute(
  383.                 self::ROUTES__FOLDER,
  384.                 array(
  385.                     'containerId' => $last->getContainer()->getId(),
  386.                     'folderId' => $last->getId(),
  387.                 )
  388.             );
  389.         } else if ($last instanceof Container && ! empty($container) && $this->compareLastToCurrent($last$container)) {
  390.             // redirect to last container's view
  391.             return $this->redirectToRoute(
  392.                 self::ROUTES__CONTAINER,
  393.                 array(
  394.                     'containerId' => $last->getId(),
  395.                 )
  396.             );
  397.         } else if ( ! empty($container)) {
  398.             // redirect to the root view of the container
  399.             return $this->redirectToRoute(
  400.                 self::ROUTES__CONTAINER,
  401.                 array(
  402.                     'containerId' => $container->getId(),
  403.                 )
  404.             );
  405.         }
  406.         return $this->viewAjax(
  407.             array(
  408.                 'containers' => $this->getEntityManager()->getRepository(Container::class)->findAllHierarchy(),
  409.             )
  410.         );
  411.     }
  412.     private function compareLastToCurrent(Container $lastContainer $current)
  413.     {
  414.         switch (true) {
  415.             // if both are generic, just allow
  416.             case $last instanceof GenericContainer && $current instanceof GenericContainer:
  417.                 return true;
  418.             // if we are in an intranet for the first time, really don't want to jump to a public one
  419.             case $last instanceof GenericContainer && $current instanceof IntranetContainer:
  420.                 return false;
  421.             // personal containers are not allowed to cross with anything
  422.             case $last instanceof GenericContainer && $current instanceof PersonalContainer:
  423.                 return false;
  424.             // cannot pull intranet stuff from public
  425.             case $last instanceof IntranetContainer && $current instanceof GenericContainer:
  426.                 return false;
  427.             // allow intranet of same type
  428.             case $last instanceof IntranetContainer && $current instanceof IntranetContainer:
  429.                 return true;
  430.             // personal containers are not allowed to cross with anything
  431.             case $last instanceof IntranetContainer && $current instanceof PersonalContainer:
  432.                 return false;
  433.             // personal containers are not allowed to cross with anything
  434.             case $last instanceof PersonalContainer && $current instanceof GenericContainer:
  435.                 return false;
  436.             // personal containers are not allowed to cross with anything
  437.             case $last instanceof PersonalContainer && $current instanceof IntranetContainer:
  438.                 return false;
  439.             // allow only if the containers are owned by the same person
  440.             case $last instanceof PersonalContainer && $current instanceof PersonalContainer:
  441.                 return ($last->getAccount() === $current->getAccount());
  442.             // if both are storage, they must be of the same storage type
  443.             case $last instanceof StorageContainer && $current instanceof StorageContainer:
  444.                 return ($last->getSlug() === $current->getSlug());
  445.             // TODO: finish all storage container checks
  446.         }
  447.         // this function should cover all cases, but if not, assume false
  448.         return false;
  449.     }
  450.     /**
  451.      * @param string|null $sort
  452.      * @param string|null $direction
  453.      * @return array
  454.      */
  455.     protected function fixOrdering($sort null$direction null)
  456.     {
  457.         // get the default params
  458.         $param $this->getModalParam(self::PARAMS__SORT);
  459.         // get the session value
  460.         $session $this->getSession()->get(self::SESSION__SORT);
  461.         // generate the given value
  462.         $given $sort;
  463.         if ( ! empty($given)) {
  464.             $given .= '/' . (( ! empty($direction)) ? $direction 'asc');
  465.         }
  466.         // decide based on lookup chain
  467.         $ordering $given;
  468.         if (empty($ordering)) {
  469.             $ordering $session;
  470.         }
  471.         if (empty($ordering)) {
  472.             $ordering $param;
  473.         }
  474.         if (empty($ordering)) {
  475.             $ordering 'name/asc';
  476.         }
  477.         // save most recent ordering in session
  478.         $this->getSession()->set(self::SESSION__SORT$ordering);
  479.         return explode('/'$ordering);
  480.     }
  481.     /**
  482.      * @param string $sort
  483.      * @param string $direction
  484.      * @param bool $files
  485.      * @return array
  486.      */
  487.     protected function generateOrdering($sort$direction$files false)
  488.     {
  489.         // fix into orm ordering array
  490.         $direction strtoupper($direction);
  491.         switch ($sort) {
  492.             case 'name':
  493.                 $ordering = array(
  494.                     'name' => $direction,
  495.                 );
  496.                 if ($files === true) {
  497.                     $ordering['extension'] = $direction;
  498.                 }
  499.                 break;
  500.             case 'timestamp':
  501.                 $ordering = array(
  502.                     (($files === true) ? 'uploadedAt' 'touchedAt') => $direction,
  503.                 );
  504.                 if ($files === true) {
  505.                     $ordering['name'] = 'ASC';
  506.                     $ordering['extension'] = 'ASC';
  507.                 }
  508.                 break;
  509.             default:
  510.                 throw new \Exception();
  511.         }
  512.         return $ordering;
  513.     }
  514.     /**
  515.      * @param string $containerId
  516.      * @param string|null $sort
  517.      * @param string|null $direction
  518.      * @return AjaxScene
  519.      *
  520.      * @Route(
  521.      *  "/{containerId}",
  522.      *  name = ModalController::ROUTES__CONTAINER,
  523.      *  requirements = {
  524.      *      "containerId" = "[1-9]\d*"
  525.      *  }
  526.      * )
  527.      *
  528.      * @Route(
  529.      *  "/{containerId}/{sort}/{direction}",
  530.      *  name = ModalController::ROUTES__CONTAINER_SORT,
  531.      *  requirements = {
  532.      *      "containerId" = "[1-9]\d*",
  533.      *      "sort" = "name|timestamp",
  534.      *      "direction" = "asc|desc"
  535.      *  }
  536.      * )
  537.      */
  538.     public function containerAction($containerId$sort null$direction null)
  539.     {
  540.         // get objects
  541.         $container $this->getEntityManager()->getRepository(Container::class)->findExact($containerId);
  542.         $containers $this->assembleDepartments(
  543.             ( ! empty($this->getModalParam(self::PARAMS__CONTAINER)))
  544.                 ? $this->getEntityManager()->getRepository(Container::class)->findExact($this->getModalParam(self::PARAMS__CONTAINER))
  545.                 : $container
  546.         );
  547.         // get root folders for this container
  548.         [$sort$direction] = $this->fixOrdering($sort$direction);
  549.         $folders $this->getEntityManager()->getRepository(Folder::class)->findRoots(
  550.             $container,
  551.             $this->generateOrdering($sort$direction)
  552.         );
  553.         // set the last location
  554.         $this->getSession()->set(self::SESSION__LAST_LOCATION$container->getId());
  555.         $this->getSession()->set(self::SESSION__LAST_LOCATION_TYPEClassUtils::getClass($container));
  556.         // done
  557.         return $this->viewAjax(
  558.             array(
  559.                 'containers' => $containers,
  560.                 'container' => $container,
  561.                 'folders' => $folders,
  562.                 'sort' => $sort,
  563.                 'direction' => $direction,
  564.                 'ancestors' => [],
  565.                 'folder' => null,
  566.                 'parent' => null,
  567.                 'files' => [],
  568.             )
  569.         );
  570.     }
  571.     /**
  572.      * @param Folder $folder
  573.      * @param array $ordering
  574.      * @return array|File[]
  575.      */
  576.     private function queryFiles(Folder $folder, array $ordering = [])
  577.     {
  578.         // default if none given
  579.         if (empty($ordering)) {
  580.             $ordering = array(
  581.                 'name' => 'ASC',
  582.                 'extension' => 'ASC',
  583.             );
  584.         }
  585.         // see if we have a type set for our modal
  586.         if (empty($this->getModalParam(self::PARAMS__TYPE))) {
  587.             // we do not, use generic file repository
  588.             $repo $this->getEntityManager()->getRepository(File::class);
  589.         } else {
  590.             // try and get the specific type
  591.             $class sprintf(
  592.                 'Cms\\FileBundle\\Entity\\Nodes\\Files\\%sFile',
  593.                 $this->getModalParam(self::PARAMS__TYPE)
  594.             );
  595.             // see if our file type exists
  596.             if ( ! class_exists($class)) {
  597.                 // use the base again
  598.                 $repo $this->getEntityManager()->getRepository(File::class);
  599.             } else {
  600.                 // use it's repo
  601.                 /** @var FileRepository $repo */
  602.                 $repo $this->getEntityManager()->getRepository($class);
  603.             }
  604.         }
  605.         
  606.         // use the repo to query for files
  607.         return $repo->findBy(
  608.             array(
  609.                 'parent' => $folder->getId(),
  610.                 'container' => $folder->getContainer(),
  611.             ),
  612.             $ordering
  613.         );
  614.     }
  615.     /**
  616.      * @param string $containerId
  617.      * @param string $folderId
  618.      * @param string|null $sort
  619.      * @param string|null $direction
  620.      * @return AjaxScene
  621.      * @throws \Exception
  622.      *
  623.      * @Route(
  624.      *  "/{containerId}/{folderId}",
  625.      *  name = ModalController::ROUTES__FOLDER,
  626.      *  requirements = {
  627.      *      "containerId" = "[1-9]\d*",
  628.      *      "folderId" = "[1-9]\d*"
  629.      *  }
  630.      * )
  631.      *
  632.      * @Route(
  633.      *  "/{containerId}/{folderId}/sort/{sort}/{direction}",
  634.      *  name = ModalController::ROUTES__FOLDER_SORT,
  635.      *  requirements = {
  636.      *      "containerId" = "[1-9]\d*",
  637.      *      "folderId" = "[1-9]\d*",
  638.      *      "sort" = "name|timestamp",
  639.      *      "direction" = "asc|desc"
  640.      *  }
  641.      * )
  642.      */
  643.     public function folderAction($containerId$folderId$sort null$direction null)
  644.     {
  645.         // get objects and validate associations
  646.         $container $this->getEntityManager()->getRepository(Container::class)->findExact($containerId);
  647.         $containers $this->assembleDepartments(
  648.             ( ! empty($this->getModalParam(self::PARAMS__CONTAINER)))
  649.                 ? $this->getEntityManager()->getRepository(Container::class)->findExact($this->getModalParam(self::PARAMS__CONTAINER))
  650.                 : $container
  651.         );
  652.         $folder $this->getEntityManager()->getRepository(Folder::class)->findExact($folderId);
  653.         if ($folder->getContainer() !== $container) {
  654.             throw new \Exception();
  655.         }
  656.         // get the ancestor folders
  657.         $ancestors $this->getEntityManager()->getRepository(Folder::class)->findAncestors($folder);
  658.         // fix sorting vars
  659.         [$sort$direction] = $this->fixOrdering($sort$direction);
  660.         // get subfolders
  661.         $folders $this->getEntityManager()->getRepository(Folder::class)->findImmediateChildren(
  662.             $folder,
  663.             $this->generateOrdering($sort$direction)
  664.         );
  665.         
  666.         // get files, be sure to account for specific type if set in modal params
  667.         $files $this->queryFiles(
  668.             $folder,
  669.             $this->generateOrdering($sort$directiontrue)
  670.         );
  671.         // set last location
  672.         $this->getSession()->set(self::SESSION__LAST_LOCATION$folder->getId());
  673.         $this->getSession()->set(self::SESSION__LAST_LOCATION_TYPEClassUtils::getClass($folder));
  674.         // done
  675.         return $this->viewAjax(
  676.             array(
  677.                 'containers' => $containers,
  678.                 'container' => $container,
  679.                 'folder' => $folder,
  680.                 'ancestors' => $ancestors,
  681.                 'folders' => $folders,
  682.                 'files' => $files,
  683.                 'routes' => array(
  684.                     'crop' => DashboardController::ROUTES__CROP
  685.                 ),
  686.                 'sort' => $sort,
  687.                 'direction' => $direction,
  688.             )
  689.         );
  690.     }
  691.     /**
  692.      * @param Request $request
  693.      * @param string $containerId
  694.      * @param string $folderId
  695.      * @return AjaxScene|RedirectResponse
  696.      * @throws \Exception
  697.      *
  698.      * @Route(
  699.      *  "/{containerId}/create-folder",
  700.      *  name = ModalController::ROUTES__FOLDER_ROOT_CREATE,
  701.      *  requirements = {
  702.      *      "containerId" = "[1-9]\d*"
  703.      *  }
  704.      * )
  705.      * @Route(
  706.      *  "/{containerId}/{folderId}/create-folder",
  707.      *  name = ModalController::ROUTES__FOLDER_CHILD_CREATE,
  708.      *  requirements = {
  709.      *      "containerId" = "[1-9]\d*",
  710.      *      "folderId" = "[1-9]\d*"
  711.      *  }
  712.      * )
  713.      */
  714.     public function folderCreateAction(Request $request$containerId$folderId null)
  715.     {
  716.         // get objects and validate associations
  717.         $container $this->getEntityManager()->getRepository(Container::class)->findExact($containerId);
  718.         $containers $this->assembleDepartments(
  719.             ( ! empty($this->getModalParam(self::PARAMS__CONTAINER)))
  720.                 ? $this->getEntityManager()->getRepository(Container::class)->findExact($this->getModalParam(self::PARAMS__CONTAINER))
  721.                 : $container
  722.         );
  723.         // AUDIT
  724.         $this->denyAccessUnlessGranted(
  725.             array_filter(array(
  726.                 'campussuite.cms.file.manage',
  727.                 ($container instanceof StorageContainer && $container->getSlug() === 'smm')
  728.                     ? 'campussuite.smm.manage'
  729.                     null,
  730.             )),
  731.             array($container$container)
  732.         );
  733.         $parent null;
  734.         if ($folderId !== null) {
  735.             $parent $this->getEntityManager()->getRepository(Folder::class)->findExact($folderId);
  736.             if ($parent->getContainer() !== $container) {
  737.                 throw new \Exception();
  738.             }
  739.         }
  740.         // get the ancestor folders
  741.         $ancestors = [];
  742.         if ($parent !== null) {
  743.             $ancestors $this->getEntityManager()->getRepository(Folder::class)->findAncestors($parent);
  744.         }
  745.         // generate object and form
  746.         $folder = new Folder();
  747.         $form $this->createForm(FolderType::class, $folder, []);
  748.         $form->remove('description');
  749.         // handle submission
  750.         if ($this->handleForm($form$request)) {
  751.             // enforce things
  752.             $folder
  753.                 ->setContainer($container)
  754.                 ->setParent($parent);
  755.             // save and log
  756.             $this->getEntityManager()->save($folder);
  757.             // record log
  758.             $this->getActivityLogger()->createLog($folder);
  759.             // redirect to new folders view
  760.             return $this->redirectToRoute(
  761.                 self::ROUTES__FOLDER,
  762.                 array(
  763.                     'containerId' => $container->getId(),
  764.                     'folderId' => $folder->getId(),
  765.                 )
  766.             );
  767.         }
  768.         // done
  769.         return $this->viewAjax(
  770.             array(
  771.                 'containers' => $containers,
  772.                 'container' => $container,
  773.                 'folder' => $parent,
  774.                 'ancestors' => $ancestors,
  775.                 'form' => $form->createView(),
  776.             )
  777.         );
  778.     }
  779.     /**
  780.      * @param string $containerId
  781.      * @param string $folderId
  782.      * @return AjaxScene
  783.      * @throws \Exception
  784.      *
  785.      * @Route(
  786.      *  "/{containerId}/{folderId}/upload",
  787.      *  name = ModalController::ROUTES__UPLOAD,
  788.      *  requirements = {
  789.      *      "containerId" = "[1-9]\d*",
  790.      *      "folderId" = "[1-9]\d*"
  791.      *  }
  792.      * )
  793.      */
  794.     public function uploadAction($containerId$folderId)
  795.     {
  796.         // get and validate objects
  797.         $container $this->getEntityManager()->getRepository(Container::class)->findExact($containerId);
  798.         $containers $this->assembleDepartments(
  799.             ( ! empty($this->getModalParam(self::PARAMS__CONTAINER)))
  800.                 ? $this->getEntityManager()->getRepository(Container::class)->findExact($this->getModalParam(self::PARAMS__CONTAINER))
  801.                 : $container
  802.         );
  803.         // AUDIT
  804.         $this->denyAccessUnlessGranted(
  805.             array_filter(array(
  806.                 'campussuite.cms.file.manage',
  807.                 ($container instanceof StorageContainer && $container->getSlug() === 'smm')
  808.                     ? 'campussuite.smm.manage'
  809.                     null,
  810.             )),
  811.             array($container$container)
  812.         );
  813.         $folder $this->getEntityManager()->getRepository(Folder::class)->findExact($folderId);
  814.         if ($folder->getContainer() !== $container) {
  815.             throw new \Exception();
  816.         }
  817.         // get the ancestor folders
  818.         $ancestors $this->getEntityManager()->getRepository(Folder::class)->findAncestors($folder);
  819.         // just show view
  820.         return $this->viewAjax(
  821.             array(
  822.                 'containers' => $containers,
  823.                 'container' => $container,
  824.                 'folder' => $folder,
  825.                 'ancestors' => $ancestors,
  826.             )
  827.         );
  828.     }
  829.     /**
  830.      * @param string $containerId
  831.      * @return AjaxScene
  832.      * @throws \Exception
  833.      *
  834.      * @Route(
  835.      *  "/url/{containerId}",
  836.      *  name = ModalController::ROUTES__URL,
  837.      *  requirements = {
  838.      *      "containerId" = "[1-9]\d*"
  839.      *  },
  840.      *  defaults = {
  841.      *      "containerId" = null
  842.      *  }
  843.      * )
  844.      */
  845.     public function urlAction($containerId null)
  846.     {
  847.         // make sure we support this
  848.         if ($this->getModalParam(self::PARAMS__MODE) === self::MODES__ID) {
  849.             throw new \Exception();
  850.         }
  851.         // get and validate objects
  852.         $container null;
  853.         if ( ! empty($containerId)) {
  854.             $container $this->getEntityManager()->getRepository(Container::class)->findExact($containerId);
  855.         } else if ( ! empty($this->getSession()->get(self::SESSION__LAST_LOCATION))) {
  856.             $last $this->getEntityManager()
  857.                 ->getRepository($this->getSession()->get(self::SESSION__LAST_LOCATION_TYPE))
  858.                 ->findExact($this->getSession()->get(self::SESSION__LAST_LOCATION));
  859.             switch (true) {
  860.                 case $last instanceof Folder:
  861.                     $container $last->getContainer();
  862.                     break;
  863.                 case $last instanceof Container:
  864.                     $container $last;
  865.                     break;
  866.             }
  867.         } else if ( ! empty($this->getModalParam(self::PARAMS__CONTAINER))) {
  868.             $container $this->getEntityManager()->getRepository(Container::class)->findExact($this->getModalParam(self::PARAMS__CONTAINER));
  869.         }
  870.         $containers $this->assembleDepartments(
  871.             ( ! empty($this->getModalParam(self::PARAMS__CONTAINER)))
  872.                 ? $this->getEntityManager()->getRepository(Container::class)->findExact($this->getModalParam(self::PARAMS__CONTAINER))
  873.                 : $container
  874.         );
  875.         // create form
  876.         $form $this->createFormBuilder(
  877.             array(
  878.                 'url' => $this->getModalParam(self::PARAMS__VALUE),
  879.             ),
  880.             []
  881.         )
  882.             ->add('url'TextType::class, [])
  883.             ->getForm();
  884.         // just show view
  885.         return $this->viewAjax(
  886.             array(
  887.                 'containers' => $containers,
  888.                 'container' => $container,
  889.                 'form' => $form->createView(),
  890.             )
  891.         );
  892.     }
  893.     /**
  894.      * {@inheritdoc}
  895.      */
  896.     public function viewAjax($template null, array $parameters = []): AjaxScene
  897.     {
  898.         $view parent::viewAjax($template$parameters);
  899.         $view->setParameters(array_merge(
  900.             $view->getParameters(),
  901.             array(
  902.                 'modal' => array_merge(
  903.                     $this->getModalParams(),
  904.                     array(
  905.                         'entity' => ( ! empty($this->getModalParam('container'))) ? $this->getEntityManager()->getRepository(Container::class)->findExact(
  906.                             $this->getModalParam('container')
  907.                         ) : null,
  908.                     )
  909.                 ),
  910.             )
  911.         ));
  912.         return $view;
  913.     }
  914.     /**
  915.      * @param Container $current
  916.      * @return array|Container[]
  917.      */
  918.     private function assembleDepartments(Container $current)
  919.     {
  920.         // query based on class
  921.         // get only ones in same root
  922.         // and order by the left values
  923.         /** @var array|Container[] $containers */
  924.         $containers array_merge(
  925.             $this->getEntityManager()->getRepository(ClassUtils::getClass($current))->findBy(
  926.                 ($current instanceof PersonalContainer)
  927.                     ? array(
  928.                         'account' => $this->getGlobalContext()->getEffectiveAccount(),
  929.                     )
  930.                     : [],
  931.                 array(
  932.                     'rt' => 'ASC',
  933.                     'lft' => 'ASC',
  934.                     'name' => 'ASC',
  935.                 )
  936.             ),
  937.             ($current instanceof IntranetContainer || $current instanceof StorageContainer)
  938.                 ? $this->getEntityManager()->getRepository(GenericContainer::class)->findBy(
  939.                     [],
  940.                     array(
  941.                         'rt' => 'ASC',
  942.                         'lft' => 'ASC',
  943.                         'name' => 'ASC',
  944.                     )
  945.                 )
  946.                 : []
  947.         );
  948.         return $containers;
  949.     }
  950.     /**
  951.      * @return S3Wrapper|object
  952.      */
  953.     private function getS3Wrapper(): S3Wrapper
  954.     {
  955.         return $this->get(__METHOD__);
  956.     }
  957.     /**
  958.      * @return FileManager|object
  959.      */
  960.     private function getFileManager(): FileManager
  961.     {
  962.         return $this->get(__METHOD__);
  963.     }
  964. }