src/Cms/ContainerBundle/Controller/ContainerController.php line 710

Open in your IDE?
  1. <?php
  2. namespace Cms\ContainerBundle\Controller;
  3. use App\Component\Blobs\BlobManagers\DepartmentBlobManager;
  4. use Cms\ContainerBundle\Doctrine\ContainerRepository;
  5. use Cms\ContainerBundle\Doctrine\NestedContainerRepository;
  6. use Cms\ContainerBundle\Entity\Container;
  7. use Cms\ContainerBundle\Entity\Containers\GenericContainer;
  8. use Cms\ContainerBundle\Entity\Containers\IntranetContainer;
  9. use Cms\ContainerBundle\Entity\Containers\PersonalContainer;
  10. use Cms\ContainerBundle\Form\Type\Container\GenericContainerType;
  11. use Cms\ContainerBundle\Form\Type\Container\IntranetContainerType;
  12. use Cms\ContainerBundle\Form\Type\Container\PersonalContainerType;
  13. use Cms\ContainerBundle\Form\Type\ContainerFaviconType;
  14. use Cms\ContainerBundle\Form\Type\ContainerRobotsType;
  15. use Cms\ContainerBundle\Form\Type\ContainerSitemapType;
  16. use Cms\ContainerBundle\Form\Type\ContainerThemeType;
  17. use Cms\ContainerBundle\Service\ContainerService;
  18. use Cms\CoreBundle\Model\Scenes\DashboardScenes\DocumentScene;
  19. use Cms\ContainerBundle\Form\Type\ContainerAssignType;
  20. use Cms\ContainerBundle\Form\Type\ContainerMoveType;
  21. use Cms\CoreBundle\Util\Controller;
  22. use Cms\CoreBundle\Util\DateTimeUtils;
  23. use Cms\CoreBundle\Util\Doctrine\EntityManager;
  24. use Cms\FileBundle\Controller\DashboardController as CmsDashboardController;
  25. use Cms\FileBundle\Entity\Nodes\Folder;
  26. use Cms\FrontendBundle\Controller\CatchallController;
  27. use Cms\ModuleBundle\Controller\ContentController;
  28. use Cms\Modules\PageBundle\Entity\Page\PageHistory;
  29. use Cms\Modules\PageBundle\Entity\Page\PageProxy;
  30. use Cms\NavigationBundle\Controller\DefaultController;
  31. use Cms\SitebuilderBundle\Model\DeployOptions;
  32. use Cms\SitebuilderBundle\Service\WorksheetBuilder;
  33. use Cms\SitebuilderBundle\Util\Deployer;
  34. use Doctrine\Common\Util\ClassUtils;
  35. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  36. use Symfony\Component\Routing\Annotation\Route;
  37. use Symfony\Component\Form\FormInterface;
  38. use Symfony\Component\HttpFoundation\RedirectResponse;
  39. use Symfony\Component\HttpFoundation\Request;
  40. use Symfony\Component\HttpFoundation\Response;
  41. use Symfony\Component\HttpFoundation\ResponseHeaderBag;
  42. /**
  43.  * Class ContainerController
  44.  * @package Cms\ContainerBundle\Controller
  45.  */
  46. final class ContainerController extends Controller
  47. {
  48.     const ROUTES__INDEX 'cms.container.dashboard.container.index';
  49.     const ROUTES__INDEX_GENERIC 'cms.container.dashboard.container.index_generic';
  50.     const ROUTES__INDEX_PERSONAL 'cms.container.dashboard.container.index_personal';
  51.     const ROUTES__INDEX_INTRANET 'cms.container.dashboard.container.index_intranet';
  52.     const ROUTES__VIEW 'cms.container.dashboard.container.view';
  53.     const ROUTES__CREATE_TYPED 'cms.container.dashboard.container.create_typed';
  54.     const ROUTES__CREATE_CHILD 'cms.container.dashboard.container.create_child';
  55.     const ROUTES__EDIT 'cms.container.dashboard.container.edit';
  56.     const ROUTES__TOGGLE_HIDDEN 'cms.container.dashboard.container.toggle_hidden';
  57.     const ROUTES__MOVE 'cms.container.dashboard.container.move';
  58.     const ROUTES__DELETE 'cms.container.dashboard.container.delete';
  59.     const ROUTES__MODULES 'cms.container.dashboard.container.modules';
  60.     const ROUTES__ASSIGN 'cms.container.dashboard.container.assign';
  61.     const ROUTES__FAVICON 'cms.container.dashboard.domain.favicon';
  62.     const ROUTES__ROBOTS 'cms.container.dashboard.domain.robots';
  63.     const ROUTES__SITEMAP 'cms.container.dashboard.domain.sitemap';
  64.     const ROUTES__NAVIGATION DefaultController::ROUTES__EDIT;
  65.     const ROUTES__FAVORITE_CREATE 'cms.container.dashboard.container.favorite_create';
  66.     const ROUTES__FAVORITE_DELETE 'cms.container.dashboard.container.favorite_delete';
  67.     const ROUTES__THEME 'cms.container.dashboard.container.theme';
  68.     const ROUTES__WORKSHEET 'cms.container.dashboard.container.worksheet';
  69.     /**
  70.      * @return ContainerRepository
  71.      */
  72.     private function getContainerRepository()
  73.     {
  74.         return $this->getEntityManager()->getRepository(Container::class);
  75.     }
  76.     /**
  77.      * @param Container $container
  78.      */
  79.     private function reorder(Container $container)
  80.     {
  81.         // get the gedmo tree repo for containers
  82.         /** @var NestedContainerRepository $repo */
  83.         $repo $this->getEntityManager()->getCustomRepository(
  84.             NestedContainerRepository::class,
  85.             Container::class
  86.         );
  87.         // NOTE: it is important that we do not verify, otherwise it would take a long time to complete
  88.         $repo->reorder($container'name''ASC'false);
  89.     }
  90.     /**
  91.      * @param string $containerId
  92.      * @return Response
  93.      *
  94.      * @Route(
  95.      *  "/{containerId}/worksheet",
  96.      *  name = ContainerController::ROUTES__WORKSHEET,
  97.      *  requirements = {
  98.      *      "containerId" = "[1-9]\d*"
  99.      *  }
  100.      * )
  101.      */
  102.     public function worksheetAction($containerId)
  103.     {
  104.         // get us
  105.         $container $this->getContainerRepository()->findExact($containerId);
  106.         // AUDIT
  107.         $this->denyAccessUnlessGranted(
  108.             'campussuite.cms.container.' $container::DISCR '.manage',
  109.             array($container$container)
  110.         );
  111.         // build
  112.         $data $this->getWorksheetBuilder()->build($container);
  113.         // done
  114.         return new Response(
  115.             $data,
  116.             200,
  117.             array(
  118.                 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  119.                 'Content-Disposition' => sprintf(
  120.                     '%s; filename="%s__worksheet__%s.xlsx"',
  121.                     ResponseHeaderBag::DISPOSITION_ATTACHMENT,
  122.                     $container->getTenant()->getSlug(),
  123.                     $container->getSlug()
  124.                 ),
  125.             )
  126.         );
  127.     }
  128.     /**
  129.      * @param Request $request
  130.      * @param string $containerId
  131.      * @return DocumentScene|RedirectResponse
  132.      *
  133.      * @Route(
  134.      *  "/{containerId}/theme",
  135.      *  name = ContainerController::ROUTES__THEME,
  136.      *  requirements = {
  137.      *      "containerId" = "[1-9]\d*"
  138.      *  }
  139.      * )
  140.      */
  141.     public function themeAction(Request $request$containerId)
  142.     {
  143.         // get us
  144.         $container $this->getContainerRepository()->findExact($containerId);
  145.         // AUDIT
  146.         $this->denyAccessUnlessGranted(
  147.             'campussuite.cms.container.' $container::DISCR '.manage',
  148.             array($container$container)
  149.         );
  150.         // get the starting theme
  151.         $theme $container->getDefaultTheme();
  152.         // create a form
  153.         $form $this->createForm(ContainerThemeType::class, $container);
  154.         // check for submission
  155.         if ($this->handleForm($form$request)) {
  156.             // if the theme changed, we need to clear the inner and outers set on the container
  157.             if ($container->getDefaultTheme() !== $theme || $container->getDefaultTheme() === null) {
  158.                 $container->setDefaultOuterLayout(null);
  159.                 if ($container->getDefaultInnerLayout() && $container->getDefaultInnerLayout()->getTheme() !== null) {
  160.                     $container->setDefaultInnerLayout(null);
  161.                 }
  162.             }
  163.             // save to db
  164.             $this->getEntityManager()->save($container);
  165.             // must log this action
  166.             $this->getActivityLogger()->createLog($container);
  167.             // redirect to the index
  168.             return $this->redirectToRoute(self::ROUTES__VIEW, array(
  169.                 'containerId' => $container->getId()
  170.             ));
  171.         }
  172.         // html view stuff
  173.         return $this->view(
  174.             array(
  175.                 'current' => $container,
  176.                 'parent' => $container->getParent(),
  177.                 'form' => $form->createView(),
  178.             )
  179.         );
  180.     }
  181.     /**
  182.      * @param Request $request
  183.      * @param string $containerId
  184.      * @return RedirectResponse
  185.      * @throws \Exception
  186.      *
  187.      * @Route(
  188.      *  "/{containerId}/like",
  189.      *  name = ContainerController::ROUTES__FAVORITE_CREATE,
  190.      *  requirements = {
  191.      *      "containerId" = "[1-9]\d*"
  192.      *  }
  193.      * )
  194.      */
  195.     public function favoriteCreateAction(Request $request$containerId)
  196.     {
  197.         // get the container
  198.         $container $this->getContainerRepository()->findExact($containerId);
  199.         // get the current account and try to find a favorite already for this
  200.         $account $this->getGlobalContext()->getEffectiveAccount();
  201.         if ( ! $account->getFavorites()->contains($container)) {
  202.             $account->addFavorite($container);
  203.             $this->getEntityManager()->save($account);
  204.         }
  205.         // attempt to redirect to where we came from, or a default location otherwise
  206.         $redirect $request->headers->get('Referer');
  207.         if (empty($redirect)) {
  208.             $redirect $this->generateUrl(
  209.                 self::ROUTES__VIEW,
  210.                 array(
  211.                     'containerId' => $containerId,
  212.                 )
  213.             );
  214.         }
  215.         // perform the redirect
  216.         return new RedirectResponse($redirect);
  217.     }
  218.     /**
  219.      * @param Request $request
  220.      * @param string $containerId
  221.      * @return RedirectResponse
  222.      * @throws \Exception
  223.      *
  224.      * @Route(
  225.      *  "/{containerId}/unlike",
  226.      *  name = ContainerController::ROUTES__FAVORITE_DELETE,
  227.      *  requirements = {
  228.      *      "containerId" = "[1-9]\d*"
  229.      *  }
  230.      * )
  231.      */
  232.     public function favoriteDeleteAction(Request $request$containerId)
  233.     {
  234.         // get the current container
  235.         $container $this->getContainerRepository()->findExact($containerId);
  236.         // try to find existing favorite
  237.         $account $this->getGlobalContext()->getEffectiveAccount();
  238.         $account->removeFavorite($container);
  239.         $this->getEntityManager()->save($account);
  240.         $redirect $request->headers->get('Referer');
  241.         if (empty($redirect)) {
  242.             $redirect $this->generateUrl(
  243.                 self::ROUTES__VIEW,
  244.                 array(
  245.                     'containerId' => $containerId,
  246.                 )
  247.             );
  248.         }
  249.         // perform the redirect
  250.         return new RedirectResponse($redirect);
  251.     }
  252.     /**
  253.      * @param string $type
  254.      * @return array|Container[]
  255.      * @throws \Exception
  256.      */
  257.     private function findRoots($type)
  258.     {
  259.         switch (true) {
  260.             case $type === GenericContainer::DISCR:
  261.                 return $this->getEntityManager()->getRepository(GenericContainer::class)->findAllRoots();
  262.             case $type === PersonalContainer::DISCR:
  263.                 return $this->getEntityManager()->getRepository(PersonalContainer::class)->findAllRootsForAccount(
  264.                     $this->getGlobalContext()->getEffectiveAccount()
  265.                 );
  266.             case $type === IntranetContainer::DISCR:
  267.                 return $this->getEntityManager()->getRepository(IntranetContainer::class)->findAllRoots();
  268.         }
  269.         throw new \Exception();
  270.     }
  271.     /**
  272.      * @param string $type
  273.      * @return DocumentScene
  274.      * @throws \Exception
  275.      *
  276.      *
  277.      * @Route(
  278.      *     "/",
  279.      *     name = ContainerController::ROUTES__INDEX,
  280.      *     defaults = {
  281.      *         "type" = "generic"
  282.      *     }
  283.      * )
  284.      * @Route(
  285.      *     "/generic",
  286.      *     name = ContainerController::ROUTES__INDEX_GENERIC,
  287.      *     defaults = {
  288.      *         "type" = "generic"
  289.      *     }
  290.      * )
  291.      * @Route(
  292.      *     "/personal",
  293.      *     name = ContainerController::ROUTES__INDEX_PERSONAL,
  294.      *     defaults = {
  295.      *         "type" = "personal"
  296.      *     }
  297.      * )
  298.      * @Route(
  299.      *     "/intranet",
  300.      *     name = ContainerController::ROUTES__INDEX_INTRANET,
  301.      *     defaults = {
  302.      *         "type" = "intranet"
  303.      *     }
  304.      * )
  305.      */
  306.     public function indexAction($type)
  307.     {
  308.         // get roots for type
  309.         $roots $this->findRoots($type);
  310.         // html work
  311.         return $this->view(
  312.             array(
  313.                 'type' => $type,
  314.                 'roots' => $roots,
  315.                 // TODO: do we need this for any view anymore?
  316.                 'routes' => array(
  317.                     'files' => CmsDashboardController::ROUTES__LIST_CHILDREN,
  318.                 ),
  319.             )
  320.         );
  321.     }
  322.     /**
  323.      * @param string $type
  324.      * @return string
  325.      * @throws \Exception
  326.      */
  327.     private function determineClass($type)
  328.     {
  329.         switch ($type) {
  330.             case GenericContainer::DISCR:
  331.                 return GenericContainer::class;
  332.             case PersonalContainer::DISCR:
  333.                 return PersonalContainer::class;
  334.             case IntranetContainer::DISCR:
  335.                 return IntranetContainer::class;
  336.         }
  337.         throw new \Exception();
  338.     }
  339.     /**
  340.      * @param string $type
  341.      * @return string
  342.      * @throws \Exception
  343.      */
  344.     private function determineForm($type)
  345.     {
  346.         switch ($type) {
  347.             case GenericContainer::DISCR:
  348.                 return GenericContainerType::class;
  349.             case PersonalContainer::DISCR:
  350.                 return PersonalContainerType::class;
  351.             case IntranetContainer::DISCR:
  352.                 return IntranetContainerType::class;
  353.         }
  354.         throw new \Exception();
  355.     }
  356.     private function onCreate(Container $containerFormInterface $form)
  357.     {
  358.         switch (true) {
  359.             // generic containers
  360.             case $container instanceof GenericContainer:
  361.             case $container instanceof IntranetContainer:
  362.                 // just generate the default things for a "blank" container
  363.                 $this->getEntityManager()->persistAll($this->generateDefaultContainerEntities($container));
  364.                 break;
  365.             // personal container stuff
  366.             case $container instanceof PersonalContainer:
  367.                 // set the account, if there is a parent, use that, otherwise, use current user
  368.                 if ( ! empty ($container->getParent())) {
  369.                     $container->setAccount($container->getParent()->getAccount());
  370.                 } else {
  371.                     $container->setAccount($this->getGlobalContext()->getEffectiveAccount());
  372.                 }
  373.                 // handle deployment of siteplan for model selection
  374.                 if (empty($container->getParent()) && $form->has('model') && ! empty($form->get('model')->getData())) {
  375.                     $this->getEntityManager()->persistAll(
  376.                         $this->getDeployer()->deploy(
  377.                             $form->get('model')->getData(),
  378.                             (new DeployOptions(PersonalContainer::DISCR))
  379.                                 ->setExisting($container)
  380.                         )
  381.                     );
  382.                 } else {
  383.                     $this->getEntityManager()->persistAll($this->generateDefaultContainerEntities($container));
  384.                 }
  385.                 break;
  386.         }
  387.     }
  388.     /**
  389.      * @param Request $request
  390.      * @param string $type
  391.      * @param string $containerId
  392.      * @return DocumentScene|RedirectResponse
  393.      * @throws \Exception
  394.      *
  395.      * @Route(
  396.      *     "/create/{type}",
  397.      *     name = ContainerController::ROUTES__CREATE_TYPED,
  398.      *     requirements = {
  399.      *         "type" = "generic|personal|intranet"
  400.      *     }
  401.      * )
  402.      * @Route(
  403.      *     "/{containerId}/create",
  404.      *     name = ContainerController::ROUTES__CREATE_CHILD,
  405.      *     requirements = {
  406.      *         "containerId" = "[0-9]+"
  407.      *     }
  408.      * )
  409.      */
  410.     public function createAction(Request $request$type null$containerId null)
  411.     {
  412.         // enforce parent if doing a child
  413.         $parent null;
  414.         if ( ! empty($containerId)) {
  415.             $parent $this->getContainerRepository()->findExact($containerId);
  416.         }
  417.         // enforce type of container getting created
  418.         $cls null;
  419.         switch (true) {
  420.             // no type and no parent, this is a problem
  421.             case (empty($type) && empty($parent)):
  422.                 throw new \Exception();
  423.             // we have a type and no parent, so creating a root of a certain type
  424.             case ( ! empty($type) && empty($parent)):
  425.                 $cls $this->determineClass($type);
  426.                 break;
  427.             // we have a parent given, child must be the parent type no matter what; but type should be empty
  428.             case (empty($type) && ! empty($parent)):
  429.                 $cls ClassUtils::getClass($parent);
  430.                 break;
  431.             // we have a parent given and a type, this is a bad combo of parameters
  432.             case ( ! empty($type) && ! empty($parent)):
  433.                 throw new \Exception();
  434.         }
  435.         // just double check typing is correct
  436.         switch (true) {
  437.             case empty($cls):
  438.             case ! class_exists($cls):
  439.             case ! is_subclass_of($clsContainer::class):
  440.             case ( ! empty($parent) && ClassUtils::getClass($parent) !== $cls):
  441.                 throw new \Exception();
  442.         }
  443.         // create new of that type
  444.         /** @var Container $new */
  445.         $new = new $cls();
  446.         // AUDIT
  447.         $this->denyAccessUnlessGranted(
  448.             array_filter([
  449.                 ($new instanceof PersonalContainer) ? 'campussuite.cms.me.sites' null,
  450.                 'campussuite.cms.container.' $new::DISCR '.manage',
  451.             ]),
  452.             $parent
  453.         );
  454.         // create a form
  455.         $form $this->createForm(
  456.             $this->determineForm($new::DISCR),
  457.             $new,
  458.             array('isRoot' => empty($parent))
  459.         );
  460.         // check for post
  461.         if ($this->handleForm($form$request)) {
  462.             // attach parent if any
  463.             if ( ! empty($parent)) {
  464.                 $new->setParent($parent);
  465.             }
  466.             // save to db in transaction as we may do other things as well
  467.             $this->getEntityManager()->transactional(
  468.                 function (EntityManager $em) use ($new$form$parent) {
  469.                     // persist the new thing being created
  470.                     $em->persist($new);
  471.                     // call any special handling logic for container creation
  472.                     $this->onCreate($new$form);
  473.                     // things should be persisted, flush them now to save them all to db
  474.                     $em->flush();
  475.                     // reorder containers so they show up in lists correctly
  476.                     $this->reorder(( ! empty($parent)) ? $parent $new);
  477.                 }
  478.             );
  479.             // must log this action
  480.             $this->getActivityLogger()->createLog($new);
  481.             // redirect based on whether we made a root or a child
  482.             if ( ! empty($parent)) {
  483.                 $redirect $this->redirectToRoute(
  484.                     self::ROUTES__VIEW,
  485.                     array(
  486.                         'containerId' => $parent->getId(),
  487.                     )
  488.                 );
  489.             } else {
  490.                 switch ($type) {
  491.                     case GenericContainer::DISCR:
  492.                         $route self::ROUTES__INDEX;
  493.                         break;
  494.                     case PersonalContainer::DISCR:
  495.                         $route self::ROUTES__INDEX_PERSONAL;
  496.                         break;
  497.                     case IntranetContainer::DISCR:
  498.                         $route self::ROUTES__INDEX_INTRANET;
  499.                         break;
  500.                     default:
  501.                         $route self::ROUTES__INDEX;
  502.                 }
  503.                 $redirect $this->redirectToRoute($route);
  504.             }
  505.             return $redirect;
  506.         }
  507.         // html view stuff
  508.         return $this->view(
  509.             array(
  510.                 'form' => $form->createView(),
  511.                 'type' => $type,
  512.                 'parent' => $parent,
  513.             )
  514.         );
  515.     }
  516.     /**
  517.      * @param string $containerId
  518.      * @return RedirectResponse
  519.      *
  520.      * @Route(
  521.      *     "/{containerId}/hidden",
  522.      *     name = ContainerController::ROUTES__TOGGLE_HIDDEN,
  523.      *     requirements = {
  524.      *         "containerId" = "[1-9]\d*"
  525.      *     }
  526.      * )
  527.      */
  528.     public function toggleHiddenAction($containerId)
  529.     {
  530.         // enforce container
  531.         $current $this->getContainerRepository()->findExact($containerId);
  532.         // AUDIT
  533.         $this->denyAccessUnlessGranted(
  534.             array(
  535.                 'campussuite.cms.container.' $current::DISCR '.manage',
  536.             ),
  537.             array($current$current)
  538.         );
  539.         // toggle and save to db
  540.         $this->getEntityManager()->save(
  541.             $current->setHidden( ! $current->isHidden())
  542.         );
  543.         // redirect back to view page
  544.         return $this->redirectToRoute(
  545.             self::ROUTES__VIEW,
  546.             array(
  547.                 'containerId' => $current->getParent()->getId(),
  548.             )
  549.         );
  550.     }
  551.     /**
  552.      * @param Request $request
  553.      * @param string $containerId
  554.      * @return DocumentScene|RedirectResponse
  555.      * @throws \Exception
  556.      *
  557.      * @Route(
  558.      *  "/{containerId}/edit",
  559.      *  name = ContainerController::ROUTES__EDIT,
  560.      *  requirements = {
  561.      *      "containerId" = "[1-9]\d*"
  562.      *  }
  563.      * )
  564.      */
  565.     public function editAction(Request $request$containerId)
  566.     {
  567.         // enforce container
  568.         $current $this->getContainerRepository()->findExact($containerId);
  569.         // AUDIT
  570.         $this->denyAccessUnlessGranted(
  571.             array(
  572.                 'campussuite.cms.container.' $current::DISCR '.manage',
  573.             ),
  574.             array($current$current)
  575.         );
  576.         // grab the parent
  577.         $parent $current->getParent();
  578.         // create a form
  579.         $form $this->createForm(
  580.             $this->determineForm($current::DISCR),
  581.             $current,
  582.             array('isRoot' => empty($parent))
  583.         );
  584.         // check for post
  585.         if ($this->handleForm($form$request)) {
  586.             // wrap in a transaction
  587.             $this->getEntityManager()->transactional(
  588.                 function (EntityManager $em) use ($current$parent) {
  589.                     // save to db
  590.                     $em->save($current);
  591.                     // handle reordering
  592.                     $this->reorder(( ! empty($parent)) ? $parent $current);
  593.                 }
  594.             );
  595.             // must log action
  596.             $this->getActivityLogger()->createLog($current);
  597.             // redirect back to view page
  598.             return $this->redirectToRoute(
  599.                 self::ROUTES__VIEW,
  600.                 array(
  601.                     'containerId' => $current->getId(),
  602.                 )
  603.             );
  604.         }
  605.         // html view stuff
  606.         return $this->view(
  607.             array(
  608.                 'current' => $current,
  609.                 'parent' => $parent,
  610.                 'form' => $form->createView(),
  611.             )
  612.         );
  613.     }
  614.     /**
  615.      * @param string $containerId
  616.      * @return DocumentScene|RedirectResponse
  617.      *
  618.      * @Route(
  619.      *  "/{containerId}",
  620.      *  name = ContainerController::ROUTES__VIEW,
  621.      *  requirements = {
  622.      *      "containerId" = "[1-9]\d*"
  623.      *  }
  624.      * )
  625.      */
  626.     public function viewAction($containerId)
  627.     {
  628.         // enforce container
  629.         $current $this->getContainerRepository()->findExact($containerId);
  630.         // get the parent
  631.         $parent $current->getParent();
  632.         // get all immediate child containers
  633.         $things $current->getChildren();
  634.         // if there are no roots, redirect to the pages listing
  635.         if (count($things) === 0) {
  636.             return $this->redirectToRoute(
  637.                 ContentController::ROUTES__PROXY_LIST,
  638.                 array(
  639.                     'container' => $containerId,
  640.                     'module' => 'page',
  641.                 )
  642.             );
  643.         }
  644.         $favorite = ($this->getGlobalContext()->getEffectiveAccount()
  645.             ->getFavorites()
  646.             ->contains($current)) ? $current null;
  647.         // get the ancestors
  648.         $ancestors $this->getContainerRepository()->findAncestors($current);
  649.         // html view stuff
  650.         return $this->view(
  651.             array(
  652.                 'all' => $this->getContainerRepository()->findAllHierarchy($current),
  653.                 'current' => $current,
  654.                 'favorites' => $this->getGlobalContext()->getEffectiveAccount()->getFavorites(),
  655.                 'favorite' => $favorite,
  656.                 'ancestors' => $ancestors,
  657.                 'parent' => $parent,
  658.                 'things' => $things,
  659.                 'routes' => array(
  660.                     'list_children' => CmsDashboardController::ROUTES__LIST_CHILDREN,
  661.                     'list_roots' => CmsDashboardController::ROUTES__LIST_ROOTS,
  662.                     'proxy_list' => ContentController::ROUTES__PROXY_LIST,
  663.                     'proxy_create' => ContentController::ROUTES__PROXY_CREATE,
  664.                     'public_personal' => CatchallController::ROUTES__PERSONAL,
  665.                 ),
  666.             )
  667.         );
  668.     }
  669.     /**
  670.      * @param Request $request
  671.      * @param string $containerId
  672.      * @return DocumentScene|RedirectResponse
  673.      * @throws \Exception
  674.      *
  675.      * @Route(
  676.      *  "/{containerId}/move",
  677.      *  name = ContainerController::ROUTES__MOVE,
  678.      *  requirements = {
  679.      *      "containerId" = "[1-9]\d*"
  680.      *  }
  681.      * )
  682.      */
  683.     public function moveAction(Request $request$containerId)
  684.     {
  685.         // find the container
  686.         $container $this->getContainerRepository()->findExact($containerId);
  687.         // AUDIT
  688.         $this->denyAccessUnlessGranted(
  689.             array(
  690.                 'campussuite.cms.container.' $container::DISCR '.manage',
  691.             ),
  692.             array($container$container)
  693.         );
  694.         // track the old parent
  695.         $old $container->getParent();
  696.         if ($old === null) {
  697.             throw new \Exception('This domain cannot be moved');
  698.         }
  699.         // build form
  700.         $form $this->createForm(ContainerMoveType::class, $container, []);
  701.         // check for post
  702.         if ($request->isMethod(Request::METHOD_POST)) {
  703.             // process form
  704.             $form->handleRequest($request);
  705.             if ($form->isSubmitted() && $form->isValid()) {
  706.                 // get the new parent
  707.                 $new $container->getParent();
  708.                 // AUDIT
  709.                 $this->denyAccessUnlessGranted(
  710.                     'campussuite.cms.container.' $new::DISCR '.manage',
  711.                     array($new$new)
  712.                 );
  713.                 // wrap in a db transaction
  714.                 $this->getEntityManager()->transactional(
  715.                     function (EntityManager $em) use ($container$old$new) {
  716.                         // save
  717.                         $em->save($container);
  718.                         // handle reordering
  719.                         if ( ! empty($old)) {
  720.                             $this->reorder($old);
  721.                         }
  722.                         $em->flush();
  723.                         if ( ! empty($new)) {
  724.                             $this->reorder($new);
  725.                         }
  726.                     }
  727.                 );
  728.                 $this->addFlash('success''Department "' $container->getName() . '" was moved.');
  729.                 // record log
  730.                 $this->getActivityLogger()->createLog($container);
  731.                 // redirect to parent index
  732.                 return $this->redirectToRoute(
  733.                     self::ROUTES__VIEW,
  734.                     array(
  735.                         'containerId' => $container->getParent()->getId()
  736.                     )
  737.                 );
  738.             }
  739.         }
  740.         // html view stuff
  741.         return $this->view(
  742.             array(
  743.                 'current' => $container,
  744.                 'form' => $form->createView(),
  745.             )
  746.         );
  747.     }
  748.     /**
  749.      * @param Request $request
  750.      * @param string $containerId
  751.      * @return DocumentScene|RedirectResponse
  752.      *
  753.      * @Route(
  754.      *  "/{containerId}/delete",
  755.      *  name = ContainerController::ROUTES__DELETE,
  756.      *  requirements = {
  757.      *      "containerId" = "[1-9]\d*"
  758.      *  }
  759.      * )
  760.      */
  761.     public function deleteAction(Request $request$containerId)
  762.     {
  763.         // find the container
  764.         $current $this->getContainerRepository()->findExact($containerId);
  765.         // AUDIT
  766.         $this->denyAccessUnlessGranted(
  767.             array(
  768.                 'campussuite.cms.container.' $current::DISCR '.manage',
  769.             ),
  770.             array($current$current)
  771.         );
  772.         // get parent and type
  773.         $type $current::DISCR;
  774.         $parent $current->getParent();
  775.         // get the direct children of this department
  776.         $children $this->getEntityManager()->getRepository(ClassUtils::getClass($current))->findBy(
  777.             array(
  778.                 'parent' => $current,
  779.             ),
  780.             array(
  781.                 'name' => 'ASC',
  782.             )
  783.         );
  784.         // check for submission
  785.         if ($this->handleSecureDelete($request)) {
  786.             // if we are a root, and we have children, prevent the deletion
  787.             if (empty($current->getParent()) && ! empty($children)) {
  788.                 throw new \Exception();
  789.             }
  790.             // wrap in a transaction
  791.             $this->getEntityManager()->transactional(
  792.                 function (EntityManager $em) use ($current$parent) {
  793.                     // run the deletion
  794.                     $em->delete($current);
  795.                     // reorder things to that they list correctly when pulling all items
  796.                     if ( ! empty($parent)) {
  797.                         $this->reorder($parent);
  798.                     }
  799.                 }
  800.             );
  801.             // record log
  802.             $this->getActivityLogger()->createLog($current$containerId);
  803.             // redirect back to index
  804.             if ( ! empty($parent)) {
  805.                 $redirect $this->redirectToRoute(
  806.                     self::ROUTES__VIEW,
  807.                     array(
  808.                         'containerId' => $parent->getId(),
  809.                     )
  810.                 );
  811.             } else {
  812.                 switch ($type) {
  813.                     case GenericContainer::DISCR:
  814.                         $route self::ROUTES__INDEX;
  815.                         break;
  816.                     case PersonalContainer::DISCR:
  817.                         $route self::ROUTES__INDEX_PERSONAL;
  818.                         break;
  819.                     case IntranetContainer::DISCR:
  820.                         $route self::ROUTES__INDEX_INTRANET;
  821.                         break;
  822.                     default:
  823.                         $route self::ROUTES__INDEX;
  824.                 }
  825.                 $redirect $this->redirectToRoute($route);
  826.             }
  827.             return $redirect;
  828.         }
  829.         // html view stuff
  830.         return $this->view(
  831.             array(
  832.                 'current' => $current,
  833.                 'parent' => $parent,
  834.                 'children' => $children,
  835.             )
  836.         );
  837.     }
  838.     /**
  839.      * @param Request $request
  840.      * @param string $containerId
  841.      * @return DocumentScene|RedirectResponse
  842.      * @throws \Exception
  843.      *
  844.      * @Route(
  845.      *  "/{containerId}/assign",
  846.      *  name = ContainerController::ROUTES__ASSIGN,
  847.      *  requirements = {
  848.      *      "containerId" = "[1-9]\d*"
  849.      *  }
  850.      * )
  851.      */
  852.     public function assignAction(Request $request$containerId)
  853.     {
  854.         // find the root container
  855.         $root $this->getContainerRepository()->findExact($containerId);
  856.         // AUDIT
  857.         $this->denyAccessUnlessGranted(
  858.             'campussuite.cms.container.' $root::DISCR '.manage',
  859.             array($root$root)
  860.         );
  861.         // only for generic type
  862.         if ( ! $root instanceof GenericContainer) {
  863.             throw new \Exception();
  864.         }
  865.         // make sure it is a root
  866.         if ( ! empty($root->getParent())) {
  867.             throw new \Exception();
  868.         }
  869.         // build form
  870.         $form $this->createForm(ContainerAssignType::class, $root, []);
  871.         // check for post
  872.         if ($this->handleForm($form$request)) {
  873.             // save changes
  874.             $this->getEntityManager()->save($root);
  875.             // record log
  876.             $this->getActivityLogger()->createLog($root);
  877.             // redirect to index
  878.             return $this->redirectToRoute(self::ROUTES__INDEX);
  879.         }
  880.         // html work
  881.         return $this->view(
  882.             array(
  883.                 'root' => $root,
  884.                 'form' => $form->createView(),
  885.             )
  886.         );
  887.     }
  888.     /**
  889.      * Operations with favicon for container.
  890.      *
  891.      * @param GenericContainer $container
  892.      * @return DocumentScene|RedirectResponse
  893.      *
  894.      * @Route(
  895.      *     "/{container}/favicon",
  896.      *     name = ContainerController::ROUTES__FAVICON,
  897.      *     requirements = {
  898.      *         "container" = "[1-9]\d*",
  899.      *     },
  900.      * )
  901.      * @ParamConverter(
  902.      *     "container",
  903.      *     class = GenericContainer::class,
  904.      * )
  905.      */
  906.     public function faviconAction(GenericContainer $container)
  907.     {
  908.         // AUDIT
  909.         $this->denyAccessUnlessGranted(
  910.             'campussuite.cms.container.' $container::DISCR '.manage',
  911.             [$container$container]
  912.         );
  913.         // generate the form
  914.         $form $this->createForm(
  915.             ContainerFaviconType::class,
  916.             [],
  917.             [
  918.                 'favicon_sizes' => DepartmentBlobManager::FAVICON_SIZES,
  919.             ]
  920.         );
  921.         // attempt to handle form
  922.         if ($this->handleForm($form)) {
  923.             // get favicon data
  924.             $data = [];
  925.             foreach (DepartmentBlobManager::FAVICON_SIZES as $key) {
  926.                 $data[$key] = $form->get($key)->getData() ?: null;
  927.             }
  928.             // save them
  929.             $this->getContainerService()->saveFavicons(
  930.                 $container,
  931.                 $data
  932.             );
  933.             // record log
  934.             $this->getActivityLogger()->createLog($container);
  935.             // back to sites index
  936.             return $this->redirectToRoute(
  937.                 self::ROUTES__FAVICON,
  938.                 [
  939.                     'container' => $container->getId(),
  940.                 ]
  941.             );
  942.         }
  943.         return $this->view(
  944.             [
  945.                 'root' => $container,
  946.                 'parent' => $container->getParent(),
  947.                 'form' => $form->createView(),
  948.                 'favicons' => DepartmentBlobManager::FAVICON_SIZES,
  949.             ]
  950.         );
  951.     }
  952.     /**
  953.      * Operations with robots.txt for domain.
  954.      *
  955.      * @param GenericContainer $container
  956.      * @return DocumentScene|RedirectResponse
  957.      *
  958.      * @Route(
  959.      *     "/{container}/robots",
  960.      *     name = ContainerController::ROUTES__ROBOTS,
  961.      *     requirements = {
  962.      *         "container" = "[1-9]\d*",
  963.      *     },
  964.      * )
  965.      * @ParamConverter(
  966.      *     "container",
  967.      *     class = GenericContainer::class,
  968.      * )
  969.      */
  970.     public function robotsAction(GenericContainer $container)
  971.     {
  972.         // AUDIT
  973.         $this->denyAccessUnlessGranted(
  974.             'campussuite.cms.container.' $container::DISCR '.manage',
  975.             [$container$container]
  976.         );
  977.         // get the departments blob manager
  978.         $blobs $this->getDepartmentBlobManager();
  979.         // create form to handle robots text
  980.         $form $this->createForm(
  981.             ContainerRobotsType::class,
  982.             [
  983.                 'content' => $blobs->getRobots($container) ?: '',
  984.             ]
  985.         );
  986.         // process the form
  987.         if ($this->handleForm($form)) {
  988.             // write the contents to s3
  989.             $blobs->uploadRobots(
  990.                 $container,
  991.                 trim($form->get('content')->getData())
  992.             );
  993.             // record log
  994.             $this->getActivityLogger()->createLog($container);
  995.             // back to index
  996.             return $this->redirectToRoute(ContainerController::ROUTES__INDEX);
  997.         }
  998.         return $this->view(
  999.             [
  1000.                 'root' => $container,
  1001.                 'parent' => $container->getParent(),
  1002.                 'form' => $form->createView(),
  1003.             ]
  1004.         );
  1005.     }
  1006.     /**
  1007.      * Operations with sitemap.xml for current domain.
  1008.      *
  1009.      * @param GenericContainer $container
  1010.      * @return DocumentScene|RedirectResponse
  1011.      *
  1012.      * @Route(
  1013.      *     "/{container}/sitemap",
  1014.      *     name = ContainerController::ROUTES__SITEMAP,
  1015.      *     requirements = {
  1016.      *         "container" = "[1-9]\d*",
  1017.      *     },
  1018.      * )
  1019.      * @ParamConverter(
  1020.      *     "container",
  1021.      *     class = GenericContainer::class,
  1022.      * )
  1023.      */
  1024.     public function sitemapAction(GenericContainer $container)
  1025.     {
  1026.         // AUDIT
  1027.         $this->denyAccessUnlessGranted(
  1028.             'campussuite.cms.container.' $container::DISCR '.manage',
  1029.             [$container$container]
  1030.         );
  1031.         // get the departments blob manager
  1032.         $blobs $this->getDepartmentBlobManager();
  1033.         // create form for sitemap management
  1034.         $form $this->createForm(
  1035.             ContainerSitemapType::class,
  1036.             [
  1037.                 'content' => $blobs->getSitemap($container) ?: '',
  1038.             ]
  1039.         );
  1040.         // handle form submission
  1041.         if ($this->handleForm($form)) {
  1042.             // write the contents to s3
  1043.             $blobs->uploadSitemap(
  1044.                 $container,
  1045.                 trim($form->get('content')->getData())
  1046.             );
  1047.             // record log
  1048.             $this->getActivityLogger()->createLog($container);
  1049.             // back to index
  1050.             return $this->redirectToRoute(ContainerController::ROUTES__INDEX);
  1051.         }
  1052.         return $this->view(
  1053.             [
  1054.                 'root' => $container,
  1055.                 'parent' => $container->getParent(),
  1056.                 'form' => $form->createView(),
  1057.             ]
  1058.         );
  1059.     }
  1060.     /**
  1061.      * Generates default data entities for new created container
  1062.      *
  1063.      * @param Container $container
  1064.      * @return array
  1065.      */
  1066.     private function generateDefaultContainerEntities(Container $container)
  1067.     {
  1068.         // create index page
  1069.         /** @var PageProxy $proxy */
  1070.         $proxy = (new PageProxy())
  1071.             ->setContainer($container)
  1072.             ->getData()
  1073.                 ->setTitle('Index')
  1074.                 ->setSlug('index')
  1075.             ->entity();
  1076.         // create first history entry for index page
  1077.         // TODO: this needs handled by the content management service when it is ready
  1078.         $history = (new PageHistory())
  1079.             ->setProxy($proxy)
  1080.             ->setTimestamp(DateTimeUtils::now())
  1081.             ->setData($proxy->getData());
  1082.         // attach history
  1083.         $proxy->setHistory($history);
  1084.         // send back objects
  1085.         return array(
  1086.             // empty folder for images
  1087.             (new Folder())
  1088.                 ->setName('images')
  1089.                 ->setContainer($container),
  1090.             // empty folder for documents
  1091.             (new Folder())
  1092.                 ->setName('documents')
  1093.                 ->setContainer($container),
  1094.             // the index page proxy and history
  1095.             $proxy,
  1096.             $history,
  1097.         );
  1098.     }
  1099.     /**
  1100.      * @return ContainerService|object
  1101.      */
  1102.     private function getContainerService(): ContainerService
  1103.     {
  1104.         return $this->get(__METHOD__);
  1105.     }
  1106.     /**
  1107.      * @return WorksheetBuilder|object
  1108.      */
  1109.     private function getWorksheetBuilder(): WorksheetBuilder
  1110.     {
  1111.         return $this->get(__METHOD__);
  1112.     }
  1113.     /**
  1114.      * @return Deployer|object
  1115.      */
  1116.     private function getDeployer(): Deployer
  1117.     {
  1118.         return $this->get(__METHOD__);
  1119.     }
  1120.     /**
  1121.      * @return DepartmentBlobManager|object
  1122.      */
  1123.     private function getDepartmentBlobManager(): DepartmentBlobManager
  1124.     {
  1125.         return $this->get(__METHOD__);
  1126.     }
  1127. }