src/Cms/ModuleBundle/Controller/ContentController.php line 803

Open in your IDE?
  1. <?php
  2. namespace Cms\ModuleBundle\Controller;
  3. use Cms\ContainerBundle\Entity\Container;
  4. use Cms\ContainerBundle\Entity\Containers\PersonalContainer;
  5. use Cms\ContainerBundle\Service\ContainerService;
  6. use Cms\CoreBundle\Doctrine\Hydrators\SingleColumnHydrator;
  7. use Cms\CoreBundle\Model\Scenes\DashboardScenes\AjaxScene;
  8. use Cms\CoreBundle\Model\Scenes\DashboardScenes\DocumentScene;
  9. use Cms\CoreBundle\Model\Search\AbstractSearcher;
  10. use Cms\CoreBundle\Model\Search\Search;
  11. use Cms\CoreBundle\Model\Search\SearchableInterface;
  12. use Cms\CoreBundle\Service\ContextManager;
  13. use Cms\CoreBundle\Service\Slugger;
  14. use Cms\CoreBundle\Service\Transcoding\Transcoder;
  15. use Cms\CoreBundle\Util\Controller;
  16. use Cms\CoreBundle\Util\Doctrine\EntityManager;
  17. use Cms\LogBundle\Service\FeedingService;
  18. use Cms\ModuleBundle\Doctrine\ModuleSettingsRepository;
  19. use Cms\ModuleBundle\Doctrine\ProxyRepository;
  20. use Cms\ModuleBundle\Entity\Draft;
  21. use Cms\ModuleBundle\Entity\History;
  22. use Cms\ModuleBundle\Entity\ModuleEntity;
  23. use Cms\ModuleBundle\Entity\ModuleSettings;
  24. use Cms\ModuleBundle\Entity\Proxy;
  25. use Cms\ModuleBundle\Entity\Revision;
  26. use Cms\ModuleBundle\Form\Type\Actions\DuplicateType;
  27. use Cms\ModuleBundle\Form\Type\Actions\MoveType;
  28. use Cms\ModuleBundle\Form\Type\ShareType;
  29. use Cms\ModuleBundle\Model\Action;
  30. use Cms\ModuleBundle\Model\Data;
  31. use Cms\ModuleBundle\Model\Interfaces\Shareable\ShareableInterface;
  32. use Cms\ModuleBundle\Model\ModuleConfig;
  33. use Cms\ModuleBundle\Service\ContentManager;
  34. use Cms\ModuleBundle\Service\DraftManager;
  35. use Cms\ModuleBundle\Service\ModuleManager;
  36. use Cms\ModuleBundle\Service\PublicationService;
  37. use Cms\ModuleBundle\Service\Search\ModuleDraftSearcher;
  38. use Cms\ModuleBundle\Service\Search\ModuleHistorySearcher;
  39. use Cms\ModuleBundle\Service\Search\ModuleRevisionSearcher;
  40. use Cms\Modules\AlertBundle\Entity\Alert\AlertDraft;
  41. use Cms\Modules\BlogBundle\Entity\Post\PostDraft;
  42. use Cms\Modules\CalendarBundle\Entity\Event\EventDraft;
  43. use Cms\Modules\GalleryBundle\Entity\Gallery\GalleryDraft;
  44. use Cms\Modules\NewsBundle\Entity\Article\ArticleDraft;
  45. use Cms\Modules\PageBundle\Entity\Page\PageDraft;
  46. use Cms\Modules\PageBundle\Entity\Page\PageProxy;
  47. use Cms\Modules\PeopleBundle\Entity\Profile\ProfileDraft;
  48. use Cms\Modules\PeopleBundle\Entity\Profile\ProfileProxy;
  49. use Cms\Modules\PeopleBundle\Service\PeopleModuleConfig;
  50. use Cms\Modules\QuestionBundle\Entity\Entry\EntryDraft;
  51. use Cms\Modules\SnippetBundle\Entity\Snippet\SnippetDraft;
  52. use Cms\SyncBundle\Model\Interfaces\Syncable\SyncableInterface;
  53. use Cms\Widgets\Html\Html;
  54. use Cms\Widgets\Row\Column;
  55. use Cms\Widgets\Row\Row;
  56. use Cms\WorkflowsBundle\Entity\Content\AlertContent;
  57. use Cms\WorkflowsBundle\Entity\Content\BlogContent;
  58. use Cms\WorkflowsBundle\Entity\Content\CalendarContent;
  59. use Cms\WorkflowsBundle\Entity\Content\GalleryContent;
  60. use Cms\WorkflowsBundle\Entity\Content\NewsContent;
  61. use Cms\WorkflowsBundle\Entity\Content\PageContent;
  62. use Cms\WorkflowsBundle\Entity\Content\PeopleContent;
  63. use Cms\WorkflowsBundle\Entity\Content\QuestionsContent;
  64. use Cms\WorkflowsBundle\Entity\Content\SnippetContent;
  65. use Cms\WorkflowsBundle\Entity\Publication\ScheduledPublication;
  66. use Cms\WorkflowsBundle\Entity\WorkflowContent;
  67. use Cms\WorkflowsBundle\Entity\WorkflowSubmission;
  68. use Cms\WorkflowsBundle\Service\Publication\Publisher;
  69. use Cms\WorkflowsBundle\Service\WorkflowsManager;
  70. use Doctrine\Common\Collections\ArrayCollection;
  71. use Doctrine\Common\Util\ClassUtils;
  72. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  73. use Symfony\Component\Routing\Annotation\Route;
  74. use Symfony\Component\Form\FormError;
  75. use Symfony\Component\Form\FormInterface;
  76. use Symfony\Component\HttpFoundation\RedirectResponse;
  77. use Symfony\Component\HttpFoundation\Request;
  78. use Symfony\Component\HttpFoundation\Response;
  79. use Symfony\Component\Security\Core\Exception\AccessDeniedException;
  80. /**
  81.  * Class ContentController
  82.  * @package Cms\ModuleBundle\Controller
  83.  *
  84.  * @Route(
  85.  *     "/{container}/content/{module}",
  86.  *     requirements = {
  87.  *         "container" = "[1-9]\d*",
  88.  *         "module" = "[a-z]+"
  89.  *     }
  90.  * )
  91.  *
  92.  * @ParamConverter(
  93.  *     "container",
  94.  *     converter = "cms__container"
  95.  * )
  96.  */
  97. final class ContentController extends Controller
  98. {
  99.     public const ROUTES__DRAFT_CLONE 'campussuite.cms.module.dashboard.content.draft_clone';
  100.     public const ROUTES__DRAFT_DELETE 'campussuite.cms.module.dashboard.content.draft_delete';
  101.     public const ROUTES__DRAFT_LIST 'campussuite.cms.module.dashboard.content.draft_list';
  102.     public const ROUTES__DRAFT_MODIFY 'campussuite.cms.module.dashboard.content.draft_modify';
  103.     public const ROUTES__DRAFT_PUBLISH 'campussuite.cms.module.dashboard.content.draft_publish';
  104.     public const ROUTES__DRAFT_PREVIEW 'campussuite.cms.module.dashboard.content.draft_preview';
  105.     public const ROUTES__HISTORY_LIST 'campussuite.cms.module.dashboard.content.history_list';
  106.     public const ROUTES__HISTORY_REVERT 'campussuite.cms.module.dashboard.content.history_revert';
  107.     public const ROUTES__HISTORY_PREVIEW 'campussuite.cms.module.dashboard.content.history_preview';
  108.     public const ROUTES__PROXY_CLONE 'campussuite.cms.module.dashboard.content.proxy_clone';
  109.     public const ROUTES__PROXY_CREATE 'campussuite.cms.module.dashboard.content.proxy_create';
  110.     public const ROUTES__PROXY_DELETE 'campussuite.cms.module.dashboard.content.proxy_delete';
  111.     public const ROUTES__PROXY_LIST 'campussuite.cms.module.dashboard.content.proxy_list';
  112.     public const ROUTES__PROXY_MODIFY 'campussuite.cms.module.dashboard.content.proxy_modify';
  113.     public const ROUTES__PROXY_AUDIT 'campussuite.cms.module.dashboard.content.proxy_audit';
  114.     public const ROUTES__PROXY_MOVE 'campussuite.cms.module.dashboard.content.proxy_move';
  115.     public const ROUTES__PROXY_SHARE 'campussuite.cms.module.dashboard.content.proxy_share';
  116.     public const ROUTES__PROXY_BULK_DELETE 'campussuite.cms.module.dashboard.content.proxy_bulk_delete';
  117.     public const ROUTES__PROXY_BULK_MOVE 'campussuite.cms.module.dashboard.content.proxy_bulk_move';
  118.     public const ROUTES__PROXY_BULK_DUPLICATE 'campussuite.cms.module.dashboard.content.proxy_bulk_duplicate';
  119.     public const ROUTES__PROXY_PREVIEW 'campussuite.cms.module.dashboard.content.proxy_preview';
  120.     public const ROUTES__REVISION_LIST 'campussuite.cms.module.dashboard.content.revision_list';
  121.     public const ROUTES__REVISION_REVERT 'campussuite.cms.module.dashboard.content.revision_revert';
  122.     public const ROUTES__REVISION_PREVIEW 'campussuite.cms.module.dashboard.content.revision_preview';
  123.     public const ROUTES__SETTINGS 'campussuite.cms.module.dashboard.content.settings';
  124.     public const ROUTES__MODIFY_CONFLICTS 'campussuite.cms.module.dashboard.content.modify_conflicts';
  125.     /**
  126.      * {@inheritdoc}
  127.      *
  128.      * Overriding the parent view so that we can add the module config to every twig rendering.
  129.      */
  130.     public function view($template null, array $parameters = []): DocumentScene
  131.     {
  132.         $view parent::view($template$parameters);
  133.         $view->setParameter('moduleConfig'$this->getModuleConfig());
  134.         if ($view->hasParameter('container') && $view->getParameter('container') instanceof Container) {
  135.             /** @var Container $container */
  136.             $container $view->getParameter('container');
  137.             $view
  138.                 ->setParameter(
  139.                     'ancestors',
  140.                     $container->getAncestors()
  141.                 )
  142.                 ->setParameter(
  143.                     'parent',
  144.                     $container->getParent()
  145.                 )
  146.                 ->setParameter(
  147.                     'favorite',
  148.                     $this->determineFavorite($container)
  149.                 );
  150.         }
  151.         return $view;
  152.     }
  153.     /**
  154.      * {@inheritdoc}
  155.      *
  156.      * Overriding the parent view, so we can add the module config to every twig rendering.
  157.      */
  158.     public function viewAjax($template null, array $parameters = []): AjaxScene
  159.     {
  160.         return parent::viewAjax($template$parameters)
  161.             ->setParameter('moduleConfig'$this->getModuleConfig());
  162.     }
  163.     /**
  164.      * Attempts to use module data tied to a request to load up the module configuration for the current module.
  165.      *
  166.      * @return ModuleConfig
  167.      */
  168.     private function getModuleConfig(): ModuleConfig
  169.     {
  170.         // get module
  171.         $module $this->getRequest()->attributes->get('module');
  172.         if (empty($module)) {
  173.             throw new \RuntimeException();
  174.         }
  175.         // can return from cache now
  176.         return $this->getModuleManager()->getModuleConfiguration($module);
  177.     }
  178.     /**
  179.      * @param Container $container
  180.      * @return Search|RedirectResponse
  181.      * @throws \Exception
  182.      */
  183.     private function parseModuleTypeSearch(Container $container)
  184.     {
  185.         // use info from the request to generate the proper search objects
  186.         // need to do in the context of the container we are currently in
  187.         return $this->getModuleConfig()->getSearcher()->handleRequest(
  188.             $this->getRequest(),
  189.             $container,
  190.         );
  191.     }
  192.     /**
  193.      * @param Search $search
  194.      * @param Container $container
  195.      * @return array|Proxy[]
  196.      * @throws \Exception
  197.      */
  198.     private function performModuleTypeSearch(Search $searchContainer $container)
  199.     {
  200.         // get the repo for the type of thing we are looking for
  201.         /** @var ProxyRepository $repo */
  202.         $repo $this->getEntityManager()->getRepository(sprintf(
  203.             'Cms\\Modules\\%sBundle\\Entity\\%s\\%sProxy',
  204.             $this->getModuleConfig()->name(),
  205.             $this->getModuleConfig()->types()[0],
  206.             $this->getModuleConfig()->types()[0]
  207.         ));
  208.         // should be searchable
  209.         if ( ! $repo instanceof SearchableInterface) {
  210.             throw new \Exception();
  211.         }
  212.         // get the module settings
  213.         $settings $this->getEntityManager()->getRepository($this->getModuleConfig()->settingsClass())->findOneBy(array(
  214.             'container' => $container,
  215.         ));
  216.         // get the results of the search in the context of the current container
  217.         return $repo->search($search$container$settings);
  218.     }
  219.     /**
  220.      * @param Container $container
  221.      * @return Container|null
  222.      */
  223.     private function determineFavorite(Container $container)
  224.     {
  225.         // TODO: the return on this does not make sense, but doing it for BC, should be fixed but involves fixing other areas as well...
  226.         if ($this->getGlobalContext()->getEffectiveAccount()->getFavorites()->contains($container)) {
  227.             return $container;
  228.         }
  229.         return null;
  230.     }
  231.     /**
  232.      * Common controller for handling quick previews of live content.
  233.      *
  234.      * @param Container $container
  235.      * @param Proxy $proxy
  236.      * @param History|null $history
  237.      * @param Draft|null $draft
  238.      * @param Revision|null $revision
  239.      * @return DocumentScene|RedirectResponse
  240.      * @throws \Exception
  241.      *
  242.      * @Route(
  243.      *     "/{proxy}/preview",
  244.      *     name = ContentController::ROUTES__PROXY_PREVIEW,
  245.      *     requirements = {
  246.      *         "proxy" = "[1-9]\d*"
  247.      *     }
  248.      * )
  249.      * @Route(
  250.      *     "/{proxy}/history/{history}/preview",
  251.      *     name = ContentController::ROUTES__HISTORY_PREVIEW,
  252.      *     requirements = {
  253.      *         "proxy" = "[1-9]\d*",
  254.      *         "history" = "[1-9]\d*"
  255.      *     }
  256.      * )
  257.      * @Route(
  258.      *     "/{proxy}/draft/{draft}/preview",
  259.      *     name = ContentController::ROUTES__DRAFT_PREVIEW,
  260.      *     requirements = {
  261.      *         "proxy" = "[1-9]\d*",
  262.      *         "draft" = "[1-9]\d*"
  263.      *     }
  264.      * )
  265.      * @Route(
  266.      *     "/{proxy}/draft/{draft}/revision/{revision}/preview",
  267.      *     name = ContentController::ROUTES__REVISION_PREVIEW,
  268.      *     requirements = {
  269.      *         "proxy" = "[1-9]\d*",
  270.      *         "draft" = "[1-9]\d*",
  271.      *         "revision" = "[1-9]\d*"
  272.      *     }
  273.      * )
  274.      *
  275.      * @ParamConverter(
  276.      *     "container",
  277.      *     converter = "cms__container"
  278.      * )
  279.      * @ParamConverter(
  280.      *     "proxy",
  281.      *     converter = "cms__module_entity"
  282.      * )
  283.      * @ParamConverter(
  284.      *     "history",
  285.      *     converter = "cms__module_entity"
  286.      * )
  287.      * @ParamConverter(
  288.      *     "draft",
  289.      *     converter = "cms__module_entity"
  290.      * )
  291.      * @ParamConverter(
  292.      *     "revision",
  293.      *     converter = "cms__module_entity"
  294.      * )
  295.      */
  296.     public function previewAction(Container $containerProxy $proxyHistory $history nullDraft $draft nullRevision $revision null)
  297.     {
  298.         // ensure that all the objects are associated properly
  299.         if (($response $this->validateObjects($container$proxy)) instanceof Response) {
  300.             return $response;
  301.         }
  302.         // determine what object we are trying to find
  303.         $content null;
  304.         $backLink null;
  305.         switch (true) {
  306.             case ! empty($revision):
  307.                 $content $revision;
  308.                 $backLink $this->generateUrl(
  309.                     self::ROUTES__REVISION_LIST,
  310.                     array(
  311.                         'container' => $container->getId(),
  312.                         'module' => $this->getModuleConfig()->key(),
  313.                         'proxy' => $proxy->getId(),
  314.                         'draft' => $draft->getId(),
  315.                     )
  316.                 );
  317.                 break;
  318.             case ! empty($draft):
  319.                 $content $draft;
  320.                 $backLink $this->generateUrl(
  321.                     self::ROUTES__DRAFT_LIST,
  322.                     array(
  323.                         'container' => $container->getId(),
  324.                         'module' => $this->getModuleConfig()->key(),
  325.                         'proxy' => $proxy->getId(),
  326.                     )
  327.                 );
  328.                 break;
  329.             case ! empty($history):
  330.                 $content $history;
  331.                 $backLink $this->generateUrl(
  332.                     self::ROUTES__HISTORY_LIST,
  333.                     array(
  334.                         'container' => $container->getId(),
  335.                         'module' => $this->getModuleConfig()->key(),
  336.                         'proxy' => $proxy->getId(),
  337.                     )
  338.                 );
  339.                 break;
  340.             case ! empty($proxy):
  341.                 $content $proxy;
  342.                 $backLink $this->generateUrl(
  343.                     self::ROUTES__PROXY_LIST,
  344.                     array(
  345.                         'container' => $container->getId(),
  346.                         'module' => $this->getModuleConfig()->key(),
  347.                     )
  348.                 );
  349.                 break;
  350.         }
  351.         if (empty($content)) {
  352.             throw new \Exception();
  353.         }
  354.         return $this->view(
  355.             array(
  356.                 'container' => $container,
  357.                 'content' => $content,
  358.                 'backLink' => $backLink,
  359.                 'proxy' => $proxy,
  360.                 'history' => $history,
  361.                 'draft' => $draft,
  362.                 'revision' => $revision,
  363.             )
  364.         );
  365.     }
  366.     /**
  367.      * @param Request $request
  368.      * @param Container $container
  369.      * @return DocumentScene|AjaxScene|RedirectResponse
  370.      * @throws \Exception
  371.      *
  372.      * @Route(
  373.      *     "",
  374.      *     name = ContentController::ROUTES__PROXY_LIST
  375.      * )
  376.      */
  377.     public function proxyListAction(Request $requestContainer $container)
  378.     {
  379.         // generate and run search
  380.         $search $this->parseModuleTypeSearch($container);
  381.         if ($search instanceof RedirectResponse) {
  382.             return $search;
  383.         }
  384.         $proxies $this->performModuleTypeSearch($search$container);
  385.         // handle lazyloading
  386.         if ($request->isXmlHttpRequest()) {
  387.             return $this->viewAjax(
  388.                 '@CmsModule/Content/includes/proxies.html.twig',
  389.                 array(
  390.                     'container' => $container,
  391.                     'search' => $search,
  392.                     'proxies' => $proxies,
  393.                 )
  394.             );
  395.         }
  396.         return $this->view(
  397.             array(
  398.                 'favorites' => $this->getGlobalContext()->getEffectiveAccount()->getFavorites(),
  399.                 'all' => $this->getEntityManager()->getRepository(Container::class)->findAllHierarchy($container),
  400.                 'container' => $container,
  401.                 'search' => $search,
  402.                 'proxies' => $proxies,
  403.             )
  404.         );
  405.     }
  406.     /**
  407.      * @param Request $request
  408.      * @param Container $container
  409.      * @return DocumentScene|RedirectResponse
  410.      * @throws \Exception
  411.      *
  412.      * @Route(
  413.      *     "/bulk/delete",
  414.      *     name = ContentController::ROUTES__PROXY_BULK_DELETE
  415.      * )
  416.      */
  417.     public function proxyBulkDeleteAction(Request $requestContainer $container)
  418.     {
  419.         // get the ids of what we are trying to delete
  420.         $ids array_values(array_filter(json_decode($request->request->get('data', []))));
  421.         if (empty($ids)) {
  422.             throw new \Exception();
  423.         }
  424.         // find all the things we want to delete
  425.         /** @var array|Proxy[] $things */
  426.         $things array_values(array_filter(array_map(
  427.             function(Proxy $proxy) use($container) {
  428.                 if ($proxy->getContainer()->getId() !== $container->getId()) {
  429.                     return null;
  430.                 }
  431.                 return $proxy;
  432.             },
  433.             $this->getRepository($this->getModuleConfig()->classesProxy())->findExacts($ids)
  434.         )));
  435.         // check all objects
  436.         foreach ($things as $thing) {
  437.             // ensure that all the objects are associated properly
  438.             if (($response $this->validateObjects($container$thing)) instanceof Response) {
  439.                 return $response;
  440.             }
  441.             // make sure we are not a share
  442.             if ($thing instanceof ShareableInterface && ! $thing->isMaster()) {
  443.                 throw new \Exception();
  444.             }
  445.             // AUDIT
  446.             $this->auditContainer($container$thing);
  447.         }
  448.         // check for submission
  449.         if ($this->handleBulk() && $this->handleSecureDelete()) {
  450.             // do as a transaction
  451.             $this->getActivityLogger()->backup();
  452.             try {
  453.                 $this->getEntityManager()->transactional(
  454.                     function(EntityManager $em) use ($things) {
  455.                         foreach ($things as $thing) {
  456.                             // record log
  457.                             $this->getActivityLogger()->createLog($thing$thing->getId());
  458.                             // do removal
  459.                             $this->getContentManager()->proxyDelete(
  460.                                 $thing
  461.                             );
  462.                         }
  463.                         // make changes
  464.                         $em->flush();
  465.                         $this->getActivityLogger()->commit();
  466.                     }
  467.                 );
  468.             } catch (\Exception $e) {
  469.                 $this->getActivityLogger()->restore();
  470.                 throw $e;
  471.             }
  472.             return $this->redirectToRoute(
  473.                 self::ROUTES__PROXY_LIST,
  474.                 array(
  475.                     'container' => $container->getId(),
  476.                     'module' => $this->getModuleConfig()->key(),
  477.                 )
  478.             );
  479.         }
  480.         return $this->view(
  481.             array(
  482.                 'container' => $container,
  483.                 'things' => $things,
  484.                 'data' => $ids,
  485.             )
  486.         );
  487.     }
  488.     /**
  489.      * @param Request $request
  490.      * @param Container $container
  491.      * @return DocumentScene|RedirectResponse
  492.      * @throws \Exception
  493.      *
  494.      * @Route(
  495.      *     "/bulk/move",
  496.      *     name = ContentController::ROUTES__PROXY_BULK_MOVE
  497.      * )
  498.      */
  499.     public function proxyBulkMoveAction(Request $requestContainer $container)
  500.     {
  501.         // get the ids of what we are trying to move
  502.         $ids array_values(array_filter(json_decode($request->request->get('data'))));
  503.         if (empty($ids)) {
  504.             throw new \Exception();
  505.         }
  506.         // find all the things we want to move
  507.         /** @var array|Proxy[] $things */
  508.         $things array_values(array_filter(array_map(
  509.             function(Proxy $proxy) use($container) {
  510.                 if ($proxy->getContainer()->getId() !== $container->getId()) {
  511.                     return null;
  512.                 }
  513.                 return $proxy;
  514.             },
  515.             $this->getRepository($this->getModuleConfig()->classesProxy())->findExacts($ids)
  516.         )));
  517.         // check all objects
  518.         foreach ($things as $thing) {
  519.             // ensure that all the objects are associated properly
  520.             if (($response $this->validateObjects($container$thing)) instanceof Response) {
  521.                 return $response;
  522.             }
  523.             // make sure we are not a share
  524.             if ($thing instanceof ShareableInterface && ! $thing->isMaster()) {
  525.                 throw new \Exception();
  526.             }
  527.             // AUDIT
  528.             $this->auditContainer($container$thing);
  529.         }
  530.         // create form
  531.         $form $this->createForm(
  532.             MoveType::class,
  533.             array(
  534.                 'container' => $container,
  535.             ),
  536.             []
  537.         );
  538.         // check for submission
  539.         if ($this->handleBulk() && $this->handleForm($form)) {
  540.             // AUDIT
  541.             // check that we have access to the target department
  542.             try {
  543.                 $this->auditContainer($form->getData()['container'], $things[0]);
  544.             } catch (\Exception $e) {
  545.                 $form->addError(new FormError(
  546.                     'You do not have permission to move content into the selected department, please choose a different department.'
  547.                 ));
  548.             }
  549.             // make sure we still have no errors
  550.             if ($form->isValid()) {
  551.                 // do as a transaction
  552.                 $this->getActivityLogger()->backup();
  553.                 try {
  554.                     $this->getEntityManager()->transactional(
  555.                         function(EntityManager $em) use ($things$form) {
  556.                             foreach ($things as $thing) {
  557.                                 // record log
  558.                                 $this->getActivityLogger()->createLog($thing$thing);
  559.                                 // do move
  560.                                 $this->getContentManager()->proxyMove(
  561.                                     $thing,
  562.                                     $form->getData()['container']
  563.                                 );
  564.                             }
  565.                             // make changes
  566.                             $em->flush();
  567.                             $this->getActivityLogger()->commit();
  568.                         }
  569.                     );
  570.                 } catch (\Exception $e) {
  571.                     $this->getActivityLogger()->restore();
  572.                     throw $e;
  573.                 }
  574.                 return $this->redirectToRoute(
  575.                     self::ROUTES__PROXY_LIST,
  576.                     array(
  577.                         'container' => $container->getId(),
  578.                         'module' => $this->getModuleConfig()->key(),
  579.                     )
  580.                 );
  581.             }
  582.         }
  583.         return $this->view(
  584.             array(
  585.                 'form' => $form->createView(),
  586.                 'container' => $container,
  587.                 'things' => $things,
  588.                 'data' => $ids,
  589.             )
  590.         );
  591.     }
  592.     /**
  593.      * @param Request $request
  594.      * @param Container $container
  595.      * @return DocumentScene|RedirectResponse
  596.      * @throws \Exception
  597.      *
  598.      * @Route(
  599.      *     "/bulk/duplicate",
  600.      *     name = ContentController::ROUTES__PROXY_BULK_DUPLICATE
  601.      * )
  602.      */
  603.     public function proxyBulkDuplicateAction(Request $requestContainer $container)
  604.     {
  605.         // get the ids of what we are trying to move
  606.         $ids array_values(array_filter(json_decode($request->request->get('data'))));
  607.         if (empty($ids)) {
  608.             throw new \Exception();
  609.         }
  610.         // find all the things we want to move
  611.         /** @var array|Proxy[] $things */
  612.         $things array_values(array_filter(array_map(
  613.             function(Proxy $proxy) use($container) {
  614.                 if ($proxy->getContainer()->getId() !== $container->getId()) {
  615.                     return null;
  616.                 }
  617.                 return $proxy;
  618.             },
  619.             $this->getRepository($this->getModuleConfig()->classesProxy())->findExacts($ids)
  620.         )));
  621.         // check all objects
  622.         foreach ($things as $thing) {
  623.             // ensure that all the objects are associated properly
  624.             if (($response $this->validateObjects($container$thing)) instanceof Response) {
  625.                 return $response;
  626.             }
  627.             // make sure we are not a share
  628.             if ($thing instanceof ShareableInterface && ! $thing->isMaster()) {
  629.                 throw new \Exception();
  630.             }
  631.             // AUDIT
  632.             $this->auditContainer($container$thing);
  633.         }
  634.         // create form
  635.         $form $this->createForm(
  636.             DuplicateType::class,
  637.             array(
  638.                 'container' => $container,
  639.             ),
  640.             []
  641.         );
  642.         // check for submission
  643.         if ($this->handleBulk() && $this->handleForm($form)) {
  644.             // do as a transaction
  645.             $this->getActivityLogger()->backup();
  646.             try {
  647.                 $this->getEntityManager()->transactional(
  648.                     function(EntityManager $em) use ($things$form) {
  649.                         foreach ($things as $thing) {
  650.                             // record log
  651.                             $this->getActivityLogger()->createLog($thing$thing);
  652.                             // do move
  653.                             $this->getContentManager()->proxyDuplicate(
  654.                                 $thing,
  655.                                 $form->getData()['container']
  656.                             );
  657.                         }
  658.                         // make changes
  659.                         $em->flush();
  660.                         $this->getActivityLogger()->commit();
  661.                     }
  662.                 );
  663.             } catch (\Exception $e) {
  664.                 $this->getActivityLogger()->restore();
  665.                 throw $e;
  666.             }
  667.             return $this->redirectToRoute(
  668.                 self::ROUTES__PROXY_LIST,
  669.                 array(
  670.                     'container' => $container->getId(),
  671.                     'module' => $this->getModuleConfig()->key(),
  672.                 )
  673.             );
  674.         }
  675.         return $this->view(
  676.             array(
  677.                 'form' => $form->createView(),
  678.                 'container' => $container,
  679.                 'things' => $things,
  680.                 'data' => $ids,
  681.             )
  682.         );
  683.     }
  684.     /**
  685.      * @param Container $department
  686.      * @param mixed $object
  687.      * @param array<string> $attributes
  688.      */
  689.     private function auditContainer(
  690.         Container $department,
  691.         $object null,
  692.         $attributes = []
  693.     ): void
  694.     {
  695.         // attach the alias for general content management to any attributes already passed in
  696.         $attributes[] = '@app.cms.access.'.$department::DISCR;
  697.         // handle somebody being allowed to manage their own people profiles
  698.         // TODO: this should only really apply to the edit action, not delete...
  699.         if (($object instanceof ProfileProxy || $object instanceof ProfileDraft) && strtolower($object->getData()->get('email')) === strtolower($this->getGlobalContext()->getEffectiveAccount()->getEmail()))
  700.         {
  701.             $attributes[] = 'campussuite.cms.me.profiles';
  702.         }
  703.         // check permissions
  704.         $this->denyAccessUnlessGranted(
  705.             $attributes,
  706.             [$object$department],
  707.         );
  708.     }
  709.     /**
  710.      * @param Request $request
  711.      * @param Container $container
  712.      * @param string|null $handler
  713.      * @return DocumentScene|RedirectResponse
  714.      * @throws \Exception
  715.      *
  716.      * @Route(
  717.      *     "/create/{handler}",
  718.      *     name = ContentController::ROUTES__PROXY_CREATE,
  719.      *     requirements = {
  720.      *         "handler" = "[a-zA-Z]+"
  721.      *     },
  722.      *     defaults = {
  723.      *         "handler" = null
  724.      *     }
  725.      * )
  726.      */
  727.     public function proxyCreateAction(
  728.         FeedingService $feedingService,
  729.         Request $request,
  730.         Container $container,
  731.         ?string $handler null
  732.     )
  733.     {
  734.         // generate a new thing
  735.         $class $this->getModuleConfig()->classesProxy();
  736.         /** @var Proxy $proxy */
  737.         $proxy = new $class();
  738.         // AUDIT
  739.         // If we pass a subject with module object, the voter logic will also check permissions for the object type, not just generic access
  740.         $this->auditContainer($container$proxy);
  741.         // TODO: this needs handled in a more dynamic way...
  742.         // see if we have any prefill data
  743.         $prefills array_filter(
  744.             $request->query->all(),
  745.             function ($key) {
  746.                 return (strpos($key'prefill_') === 0);
  747.             },
  748.             ARRAY_FILTER_USE_KEY
  749.         );
  750.         $prefilled false;
  751.         if ( ! empty($prefills)) {
  752.             switch ($this->getModuleConfig()->key()) {
  753.                 case 'page':
  754.                     /** @var PageProxy $proxy */
  755.                     foreach ($prefills as $k => $v) {
  756.                         $prefilled true;
  757.                         if ($v !== null && $v !== '') {
  758.                             switch (str_replace('prefill_'''$k)) {
  759.                                 case 'name':
  760.                                     $proxy->getData()
  761.                                         ->setTitle($v)
  762.                                         ->setSlug($this->getSlugger()->slugify($v));
  763.                                     break;
  764.                                 case 'nav':
  765.                                     $proxy->getData()->setAppendToNav( ! empty($v));
  766.                                     break;
  767.                                 case 'layout':
  768.                                     $v intval($v);
  769.                                     $content = [
  770.                                         'content' => [],
  771.                                     ];
  772.                                     switch ($v) {
  773.                                         case 2:
  774.                                             $content['content'][] = (new Row())
  775.                                                 ->setStructure('2/3 1/3')
  776.                                                 ->setColumns([
  777.                                                     (new Column())
  778.                                                         ->setChildren([
  779.                                                             (new Html())
  780.                                                                 ->setContent('<h1>Edit your heading here...</h1><p>Edit your copy for the page here...</p>'),
  781.                                                         ]),
  782.                                                     (new Column())
  783.                                                         ->setChildren([
  784.                                                             (new Html())
  785.                                                                 ->setContent('<h3>Edit your sidebar heading here...</h3><p>Edit your copy for the sidebar here...</p>'),
  786.                                                         ]),
  787.                                                 ]);
  788.                                             break;
  789.                                         case 1:
  790.                                             $content['content'][] = (new Row())
  791.                                                 ->setStructure('1/1')
  792.                                                 ->setColumns([
  793.                                                     (new Column())
  794.                                                         ->setChildren([
  795.                                                             (new Html())
  796.                                                                 ->setContent('<h1>Edit your heading here...</h1><p>Edit your copy for the page here...</p>'),
  797.                                                         ]),
  798.                                                 ]);
  799.                                             break;
  800.                                         default:
  801.                                             /*
  802.                                             $content['content'][] = (new Html())
  803.                                                 ->setContent(implode('', array_map(
  804.                                                     function ($html) {
  805.                                                         return '<p>'.$html.'</p>';
  806.                                                     },
  807.                                                     $faker->sentences()
  808.                                                 )));
  809.                                             */
  810.                                     }
  811.                                     $proxy->getData()->setContent($this->getTranscoder()->encode($contentTranscoder::WRAPPING__JSON));
  812.                                     break;
  813.                             }
  814.                         }
  815.                     }
  816.                     break;
  817.             }
  818.         }
  819.         // generate a form for content
  820.         $dataForm $this->createForm(
  821.             $this->getModuleConfig()->formClass(ClassUtils::getClass($proxy->getData())),
  822.             $proxy->getData(),
  823.             array(
  824.                 'container' => $container,
  825.                 'create' => true,
  826.                 'prefilled' => $prefilled,
  827.             )
  828.         );
  829.         // create forms for each action: immediate, new draft, draft revision, schedule
  830.         /** @var array|FormInterface[] $actionForms */
  831.         $actionForms $this->getContentManager()->assembleProxyCreateActions(
  832.             $this->getRequest(),
  833.             $container,
  834.             $this->getGlobalContext()->getEffectiveAccount()
  835.         );
  836.         // determine which action form we are talking about, if applicable
  837.         if ( ! empty($handler) && ! array_key_exists($handler$actionForms)) {
  838.             throw new \Exception();
  839.         }
  840.         // handle form submission
  841.         $valid $this->handleForm($dataForm);
  842.         if ( ! empty($handler)) {
  843.             $valid $this->handleForm($actionForms[$handler]['form']) && $valid;
  844.         }
  845.         if ($valid) {
  846.             // get stuff
  847.             /** @var Data $data */
  848.             $data $dataForm->getData();
  849.             /** @var Action $action */
  850.             $action $actionForms[$handler]['form']->getData();
  851.             // handle action
  852.             $result $this->getContentManager()->proxyCreate(
  853.                 $action,
  854.                 $container,
  855.                 $data
  856.             );
  857.             // LOG
  858.             if ($result instanceof ScheduledPublication) {
  859.                 $result $result->getContent()->getDraft();
  860.             }
  861.             $this->getActivityLogger()->createLog($result);
  862.             // RECENT FEED
  863.             $feedingService->log($result);
  864.             // jump to content if live
  865.             if ($result instanceof Proxy && $result->isJumpable()) {
  866.                 return $this->redirect(
  867.                     $this->getPublicationService()
  868.                         ->getFrontLink($resulttrue$result->getContainer())
  869.                 );
  870.             }
  871.             return $this->redirectToRoute(
  872.                 self::ROUTES__PROXY_LIST,
  873.                 array(
  874.                     'container' => $container->getId(),
  875.                     'module' => $this->getModuleConfig()->key(),
  876.                 )
  877.             );
  878.         }
  879.         return $this->view(
  880.             array(
  881.                 'container' => $container,
  882.                 'dataForm' => $dataForm->createView(),
  883.                 'actionForms' => array_map(
  884.                     function (array $data) {
  885.                         $data['form'] = $data['form']->createView();
  886.                         return $data;
  887.                     },
  888.                     $actionForms
  889.                 ),
  890.                 'autoShow' => ($prefilled === true),
  891.             )
  892.         );
  893.     }
  894.     /**
  895.      * @param Container $container
  896.      * @param Proxy $proxy
  897.      * @param string|null $handler
  898.      * @return DocumentScene|RedirectResponse|Response
  899.      * @throws \Exception
  900.      *
  901.      * @Route(
  902.      *     "/{proxy}/conflicts/{handler}",
  903.      *     name = ContentController::ROUTES__MODIFY_CONFLICTS,
  904.      *     requirements = {
  905.      *         "proxy" = "[1-9]\d*",
  906.      *         "handler" = "[-a-zA-Z]+"
  907.      *     },
  908.      *     defaults = {
  909.      *         "handler" = null
  910.      *     }
  911.      * )
  912.      *
  913.      * @ParamConverter(
  914.      *     "container",
  915.      *     converter = "cms__container"
  916.      * )
  917.      * @ParamConverter(
  918.      *     "proxy",
  919.      *     converter = "cms__module_entity"
  920.      * )
  921.      */
  922.     public function conflictsAction(Container $containerProxy $proxy$handler null)
  923.     {
  924.         // ensure that all the objects are associated properly
  925.         if (($response $this->validateObjects($container$proxy)) instanceof Response) {
  926.             return $response;
  927.         }
  928.         // make sure we are not a share
  929.         if ($proxy instanceof ShareableInterface && ! $proxy->isMaster()) {
  930.             throw new \Exception();
  931.         }
  932.         // if syncable, do not allow editing
  933.         if ($proxy instanceof SyncableInterface && $proxy->isSynced()) {
  934.             throw new \Exception();
  935.         }
  936.         // AUDIT
  937.         $this->auditContainer($container$proxy);
  938.         // if the proxy has a draft, load up a submission and possible publication
  939.         $submission null;
  940.         $publication null;
  941.         if ( ! empty($proxy->getDraft())) {
  942.             // TODO: needs to be a method for like a service probably...
  943.             switch (true) {
  944.                 case $proxy->getDraft() instanceof AlertDraft:
  945.                     $cls AlertContent::class;
  946.                     break;
  947.                 case $proxy->getDraft() instanceof ArticleDraft:
  948.                     // TODO: need to refactor code to fit naming conventions
  949.                     $cls NewsContent::class;
  950.                     break;
  951.                 case $proxy->getDraft() instanceof EntryDraft:
  952.                     // TODO: need to refactor code to fit naming conventions
  953.                     $cls QuestionsContent::class;
  954.                     break;
  955.                 case $proxy->getDraft() instanceof EventDraft:
  956.                     // TODO: need to refactor code to fit naming conventions
  957.                     $cls CalendarContent::class;
  958.                     break;
  959.                 case $proxy->getDraft() instanceof GalleryDraft:
  960.                     $cls GalleryContent::class;
  961.                     break;
  962.                 case $proxy->getDraft() instanceof PageDraft:
  963.                     $cls PageContent::class;
  964.                     break;
  965.                 case $proxy->getDraft() instanceof PostDraft:
  966.                     // TODO: need to refactor code to fit naming conventions
  967.                     $cls BlogContent::class;
  968.                     break;
  969.                 case $proxy->getDraft() instanceof ProfileDraft:
  970.                     // TODO: need to refactor code to fit naming conventions
  971.                     $cls PeopleContent::class;
  972.                     break;
  973.                 case $proxy->getDraft() instanceof SnippetDraft:
  974.                     $cls SnippetContent::class;
  975.                     break;
  976.                 default:
  977.                     throw new \Exception();
  978.             }
  979.             /** @var WorkflowContent $content */
  980.             $content $this->getEntityManager()->getRepository($cls)->findOneBy(array(
  981.                 'draft' => $proxy->getDraft(),
  982.             ));
  983.             if ( ! empty($content)) {
  984.                 // TODO: this does not work for some reason, even though there is a field for submission on the object
  985.                 //$submission = $content->getSubmission();
  986.                 $submission $this->getEntityManager()->getRepository(WorkflowSubmission::class)->findOneBy(array(
  987.                     'content' => $content,
  988.                     'status' => WorkflowSubmission::SUBMISSION_STATUSES__APPROVAL,
  989.                 ));
  990.                 // TODO: publication stuff needs improved...
  991.                 $publication $content->getPublication();
  992.                 if ( ! empty($publication) && $publication->getStatus() !== ScheduledPublication::STATUSES__SCHEDULED) {
  993.                     $publication null;
  994.                 }
  995.             }
  996.         }
  997.         // handle cases that cause us no problem
  998.         switch (true) {
  999.             // placeholder; no real proxy but draft that is not in review or anything
  1000.             case ($proxy->isPlaceholder() && empty($submission) && empty($publication)):
  1001.                 return $this->redirectToRoute(
  1002.                     self::ROUTES__DRAFT_MODIFY,
  1003.                     array_merge(
  1004.                         $this->getRequest()->query->all(),
  1005.                         $this->getRequest()->attributes->get('_route_params'),
  1006.                         array(
  1007.                             'handler' => null,
  1008.                             'draft' => $proxy->getDraft()->getId(),
  1009.                         )
  1010.                     )
  1011.                 );
  1012.             // just a proxy
  1013.             case ( ! $proxy->isPlaceholder() && empty($proxy->getDraft())):
  1014.                 return $this->redirectToRoute(
  1015.                     self::ROUTES__PROXY_MODIFY,
  1016.                     array_merge(
  1017.                         $this->getRequest()->query->all(),
  1018.                         $this->getRequest()->attributes->get('_route_params'),
  1019.                         array(
  1020.                             'handler' => null,
  1021.                         )
  1022.                     )
  1023.                 );
  1024.         }
  1025.         // generate the possible options for us
  1026.         $options = array(
  1027.             'proxy-cancel-draft' => array(
  1028.                 'label' => 'Delete Draft and Edit Live',
  1029.                 'message' => 'The current draft will be deleted and you will be brought to the live content editor.',
  1030.             ),
  1031.             'proxy-cancel-review' => array(
  1032.                 'label' => 'Cancel Review, Delete Draft, and Edit Live',
  1033.                 'message' => 'The current active draft is in review. This review will be cancelled, the draft will be deleted, and you will then be directed to the live content editor.',
  1034.             ),
  1035.             'proxy-cancel-publication' => array(
  1036.                 'label' => 'Cancel Publication, Delete Draft, and Edit Live',
  1037.                 'message' => 'The current active draft is scheduled for future publication. This publication will be cancelled, the draft will be deleted, and you will then be directed to the live content editor.',
  1038.             ),
  1039.             'draft' => array(
  1040.                 'label' => 'Edit Draft',
  1041.                 'message' => 'The current draft will stay intact, and you will be directed to the draft editor. If the draft is in active review, it will be modifed and the review will be updated.',
  1042.             ),
  1043.             'draft-cancel-review' => array(
  1044.                 'label' => 'Cancel Review and Edit Draft',
  1045.                 'message' => 'The current active draft is in review. This review will be cancelled, and you will be directed to the draft editor. The updated draft can then be re-submitted for review again later.',
  1046.             ),
  1047.             'draft-cancel-publication' => array(
  1048.                 'label' => 'Cancel Publication and Edit Draft',
  1049.                 'message' => 'The current active draft is scheduled for future publication. This publication will be cancelled, and you will be directed to the draft editor.',
  1050.             ),
  1051.         );
  1052.         // specific options for the case
  1053.         switch (true) {
  1054.             // no proxy, but with draft in review
  1055.             case ($proxy->isPlaceholder() && ! empty($submission)):
  1056.                 $options = array(
  1057.                     'draft' => $options['draft'],
  1058.                     'draft-cancel-review' => $options['draft-cancel-review'],
  1059.                 );
  1060.                 break;
  1061.             // no proxy, but with draft scheduled
  1062.             case ($proxy->isPlaceholder() && ! empty($publication)):
  1063.                 $options = array(
  1064.                     'draft-cancel-publication' => $options['draft-cancel-publication'],
  1065.                 );
  1066.                 break;
  1067.             // proxy, but with draft in review
  1068.             case ( ! empty($submission)):
  1069.                 $options = array(
  1070.                     'draft' => $options['draft'],
  1071.                     'draft-cancel-review' => $options['draft-cancel-review'],
  1072.                     'proxy-cancel-review' => $options['proxy-cancel-review'],
  1073.                 );
  1074.                 break;
  1075.             // proxy, but with draft scheduled
  1076.             case ( ! empty($publication)):
  1077.                 $options = array(
  1078.                     'draft-cancel-publication' => $options['draft-cancel-publication'],
  1079.                     'proxy-cancel-publication' => $options['proxy-cancel-publication'],
  1080.                 );
  1081.                 break;
  1082.             // proxy, but with draft
  1083.             case ( ! empty($proxy->getDraft())):
  1084.                 $options = array(
  1085.                     'draft' => $options['draft'],
  1086.                     'proxy-cancel-draft' => $options['proxy-cancel-draft'],
  1087.                 );
  1088.                 break;
  1089.         }
  1090.         // handle
  1091.         if ( ! empty($handler)) {
  1092.             // handle actions
  1093.             switch ($handler) {
  1094.                 // modify proxy, remove draft
  1095.                 case 'proxy-cancel-draft':
  1096.                     $this->getDraftManager()->closeDraft($proxy->getDraft());
  1097.                     //$this->getActivityLogger()->createLog($proxy);
  1098.                     return $this->redirectToRoute(
  1099.                         self::ROUTES__PROXY_MODIFY,
  1100.                         array_merge(
  1101.                             $this->getRequest()->query->all(),
  1102.                             $this->getRequest()->attributes->get('_route_params'),
  1103.                             array(
  1104.                                 'handler' => null,
  1105.                             )
  1106.                         )
  1107.                     );
  1108.                 // modify proxy, cancel review and draft
  1109.                 case 'proxy-cancel-review':
  1110.                     $this->getWorkflowsManager()->cancel($submission$this->getGlobalContext()->getEffectiveAccount(), null);
  1111.                     $this->getDraftManager()->closeDraft($proxy->getDraft());
  1112.                     //$this->getActivityLogger()->createLog($proxy);
  1113.                     return $this->redirectToRoute(
  1114.                         self::ROUTES__PROXY_MODIFY,
  1115.                         array_merge(
  1116.                             $this->getRequest()->query->all(),
  1117.                             $this->getRequest()->attributes->get('_route_params'),
  1118.                             array(
  1119.                                 'handler' => null,
  1120.                             )
  1121.                         )
  1122.                     );
  1123.                 // modify proxy, cancel publication and draft
  1124.                 case 'proxy-cancel-publication':
  1125.                     $this->getPublisher()->cancel($publication);
  1126.                     $this->getDraftManager()->closeDraft($proxy->getDraft());
  1127.                     //$this->getActivityLogger()->createLog($proxy);
  1128.                     return $this->redirectToRoute(
  1129.                         self::ROUTES__PROXY_MODIFY,
  1130.                         array_merge(
  1131.                             $this->getRequest()->query->all(),
  1132.                             $this->getRequest()->attributes->get('_route_params'),
  1133.                             array(
  1134.                                 'handler' => null,
  1135.                             )
  1136.                         )
  1137.                     );
  1138.                 // modify draft
  1139.                 case 'draft':
  1140.                     return $this->redirectToRoute(
  1141.                         self::ROUTES__DRAFT_MODIFY,
  1142.                         array_merge(
  1143.                             $this->getRequest()->query->all(),
  1144.                             $this->getRequest()->attributes->get('_route_params'),
  1145.                             array(
  1146.                                 'handler' => null,
  1147.                                 'draft' => $proxy->getDraft()->getId(),
  1148.                             )
  1149.                         )
  1150.                     );
  1151.                 // modify draft, but cancel review
  1152.                 case 'draft-cancel-review':
  1153.                     $this->getWorkflowsManager()->cancel($submission$this->getGlobalContext()->getEffectiveAccount(), null);
  1154.                     //$this->getActivityLogger()->createLog($proxy->getDraft());
  1155.                     return $this->redirectToRoute(
  1156.                         self::ROUTES__DRAFT_MODIFY,
  1157.                         array_merge(
  1158.                             $this->getRequest()->query->all(),
  1159.                             $this->getRequest()->attributes->get('_route_params'),
  1160.                             array(
  1161.                                 'handler' => null,
  1162.                                 'draft' => $proxy->getDraft()->getId(),
  1163.                             )
  1164.                         )
  1165.                     );
  1166.                 // modify draft, but cancel publication
  1167.                 case 'draft-cancel-publication':
  1168.                     $this->getPublisher()->cancel($publication);
  1169.                     //$this->getActivityLogger()->createLog($proxy->getDraft());
  1170.                     return $this->redirectToRoute(
  1171.                         self::ROUTES__DRAFT_MODIFY,
  1172.                         array_merge(
  1173.                             $this->getRequest()->query->all(),
  1174.                             $this->getRequest()->attributes->get('_route_params'),
  1175.                             array(
  1176.                                 'handler' => null,
  1177.                                 'draft' => $proxy->getDraft()->getId(),
  1178.                             )
  1179.                         )
  1180.                     );
  1181.             }
  1182.             // shouldn't get this far; all cases should be handled
  1183.             throw new \Exception();
  1184.         }
  1185.         return $this->view(
  1186.             array(
  1187.                 'container' => $container,
  1188.                 'proxy' => $proxy,
  1189.                 'options' => $options,
  1190.                 'preview' => $proxy->getDraft(),
  1191.                 'submission' => $submission,
  1192.                 'publication' => $publication,
  1193.             )
  1194.         );
  1195.     }
  1196.     /**
  1197.      * @param Container $container
  1198.      * @param Proxy $proxy
  1199.      * @param Draft|null $draft
  1200.      * @param string|null $handler
  1201.      * @return DocumentScene|RedirectResponse|Response
  1202.      * @throws \Exception
  1203.      *
  1204.      * @Route(
  1205.      *     "/{proxy}/modify/{handler}",
  1206.      *     name = ContentController::ROUTES__PROXY_MODIFY,
  1207.      *     requirements = {
  1208.      *         "proxy" = "[1-9]\d*",
  1209.      *         "handler" = "[a-zA-Z]+"
  1210.      *     },
  1211.      *     defaults = {
  1212.      *         "handler" = null
  1213.      *     }
  1214.      * )
  1215.      * @Route(
  1216.      *     "/{proxy}/draft/{draft}/modify/{handler}",
  1217.      *     name = ContentController::ROUTES__DRAFT_MODIFY,
  1218.      *     requirements = {
  1219.      *         "proxy" = "[1-9]\d*",
  1220.      *         "draft" = "[1-9]\d*",
  1221.      *         "handler" = "[a-zA-Z]+"
  1222.      *     },
  1223.      *     defaults = {
  1224.      *         "handler" = null
  1225.      *     }
  1226.      * )
  1227.      *
  1228.      * @ParamConverter(
  1229.      *     "container",
  1230.      *     converter = "cms__container"
  1231.      * )
  1232.      * @ParamConverter(
  1233.      *     "proxy",
  1234.      *     converter = "cms__module_entity"
  1235.      * )
  1236.      * @ParamConverter(
  1237.      *     "draft",
  1238.      *     converter = "cms__module_entity"
  1239.      * )
  1240.      */
  1241.     public function modifyAction(
  1242.         FeedingService $feedingService,
  1243.         Container $container,
  1244.         Proxy $proxy,
  1245.         Draft $draft null,
  1246.         ?string $handler null
  1247.     )
  1248.     {
  1249.         // ensure that all the objects are associated properly
  1250.         if ($draft === null) {
  1251.             if (($response $this->validateObjects($container$proxy)) instanceof Response) {
  1252.                 return $response;
  1253.             }
  1254.         } elseif (($response $this->validateObjects($container$proxy$draft)) instanceof Response) {
  1255.             return $response;
  1256.         }
  1257.         // make sure we are not a share
  1258.         if ($proxy instanceof ShareableInterface && ! $proxy->isMaster()) {
  1259.             throw new \Exception();
  1260.         }
  1261.         // if syncable, do not allow editing
  1262.         if ($proxy instanceof SyncableInterface && $proxy->isSynced()) {
  1263.             throw new \Exception();
  1264.         }
  1265.         // AUDIT
  1266.         if ( ! empty($draft)) {
  1267.             $this->auditContainer($container$draft);
  1268.         } else {
  1269.             $this->auditContainer($container$proxy);
  1270.         }
  1271.         // get object
  1272.         /** @var ModuleEntity $content */
  1273.         if ( ! empty($draft)) {
  1274.             $content = clone $draft;
  1275.         } else {
  1276.             $content = clone $proxy;
  1277.         }
  1278.         // lock the item
  1279.         $lck $this->trapLocked(( ! empty($draft)) ? $draft $proxy$this->generateUrl(
  1280.             self::ROUTES__PROXY_LIST,
  1281.             array(
  1282.                 'container' => $container->getId(),
  1283.                 'module' => $this->getModuleConfig()->key(),
  1284.             )
  1285.         ));
  1286.         if ( ! empty($lck)) {
  1287.             return $lck;
  1288.         }
  1289.         // generate a form for content
  1290.         $dataForm $this->createForm(
  1291.             $this->getModuleConfig()->formClass(ClassUtils::getClass($content->getData())),
  1292.             $content->getData(),
  1293.             array(
  1294.                 'container' => $container,
  1295.                 'create' => false,
  1296.             )
  1297.         );
  1298.         // create forms for each action: immediate, new draft, draft revision, schedule
  1299.         /** @var array|FormInterface[] $actionForms */
  1300.         if ( ! empty($draft)) {
  1301.             $actionForms $this->getContentManager()->assembleDraftModifyActions(
  1302.                 $this->getRequest(),
  1303.                 $container,
  1304.                 $this->getGlobalContext()->getEffectiveAccount(),
  1305.                 $draft
  1306.             );
  1307.         } else {
  1308.             $actionForms $this->getContentManager()->assembleProxyModifyActions(
  1309.                 $this->getRequest(),
  1310.                 $container,
  1311.                 $this->getGlobalContext()->getEffectiveAccount()
  1312.             );
  1313.         }
  1314.         // determine which action form we are talking about, if applicable
  1315.         if ( ! empty($handler) && ! array_key_exists($handler$actionForms)) {
  1316.             throw new \Exception();
  1317.         }
  1318.         // handle form submission
  1319.         $valid $this->handleForm($dataForm);
  1320.         if ( ! empty($handler)) {
  1321.             $valid $this->handleForm($actionForms[$handler]['form']) && $valid;
  1322.         }
  1323.         if ($valid) {
  1324.             // get stuff
  1325.             /** @var Data $data */
  1326.             $data $dataForm->getData();
  1327.             /** @var Action $action */
  1328.             $action $actionForms[$handler]['form']->getData();
  1329.             // handle actions
  1330.             if ( ! empty($draft)) {
  1331.                 $result $this->getContentManager()->draftModify(
  1332.                     $action,
  1333.                     $draft,
  1334.                     $data
  1335.                 );
  1336.             } else {
  1337.                 $result $this->getContentManager()->proxyModify(
  1338.                     $action,
  1339.                     $proxy,
  1340.                     $data
  1341.                 );
  1342.             }
  1343.             // release any locks
  1344.             $this->getLocksmith()->releaseLock(( ! empty($draft)) ? $draft $proxy);
  1345.             // LOG
  1346.             if ($result instanceof ScheduledPublication) {
  1347.                 $result $result->getContent()->getDraft();
  1348.             }
  1349.             $this->getActivityLogger()->createLog($result);
  1350.             // RECENT FEED
  1351.             $feedingService->log($result);
  1352.             return $this->redirectToRouteOrOverride(
  1353.                 self::ROUTES__PROXY_LIST,
  1354.                 array(
  1355.                     'container' => $container->getId(),
  1356.                     'module' => $this->getModuleConfig()->key(),
  1357.                 )
  1358.             );
  1359.         }
  1360.         return $this->view(
  1361.             array(
  1362.                 'container' => $container,
  1363.                 'proxy' => $proxy,
  1364.                 'draft' => $draft,
  1365.                 'dataForm' => $dataForm->createView(),
  1366.                 'actionForms' => array_map(
  1367.                     function (array $data) {
  1368.                         $data['form'] = $data['form']->createView();
  1369.                         return $data;
  1370.                     },
  1371.                     $actionForms
  1372.                 ),
  1373.             )
  1374.         );
  1375.     }
  1376.     /**
  1377.      * @param string $action
  1378.      * @return FormInterface
  1379.      * @throws \Exception
  1380.      */
  1381.     private function createActionForm($action)
  1382.     {
  1383.         // generate classes needed
  1384.         $formClass sprintf(
  1385.             'Cms\\ModuleBundle\\Form\\Type\\Actions\\%sActionType',
  1386.             $action
  1387.         );
  1388.         if ( ! class_exists($formClass)) {
  1389.             throw new \Exception();
  1390.         }
  1391.         $modelClass sprintf(
  1392.             'Cms\\ModuleBundle\\Model\\Actions\\%sAction',
  1393.             $action
  1394.         );
  1395.         if ( ! class_exists($modelClass)) {
  1396.             throw new \Exception();
  1397.         }
  1398.         // create and return the form
  1399.         return $this->createForm(
  1400.             $formClass,
  1401.             new $modelClass(),
  1402.             array(
  1403.             'action' => $this->generateUrl(
  1404.                 $this->getRequest()
  1405.                     ->attributes->get('_route'),
  1406.                 array_merge(
  1407.                     $this->getRequest()
  1408.                         ->attributes->get('_route_params'),
  1409.                     array(
  1410.                         'handler' => $action,
  1411.                         'redirectTo' => $this->getRequest()
  1412.                             ->query->get('redirectTo'null),
  1413.                     )
  1414.                 )
  1415.             ),
  1416.         ));
  1417.     }
  1418.     /**
  1419.      * @param Container $container
  1420.      * @param Proxy $proxy
  1421.      * @param Draft|null $draft
  1422.      * @param string|null $handler
  1423.      * @return DocumentScene|RedirectResponse
  1424.      * @throws \Exception
  1425.      *
  1426.      * @Route(
  1427.      *     "/{proxy}/clone/{handler}",
  1428.      *     name = ContentController::ROUTES__PROXY_CLONE,
  1429.      *     requirements = {
  1430.      *         "proxy" = "[1-9]\d*",
  1431.      *         "handler" = "[a-zA-Z]+"
  1432.      *     },
  1433.      *     defaults = {
  1434.      *         "handler" = null
  1435.      *     }
  1436.      * )
  1437.      * @Route(
  1438.      *     "/{proxy}/draft/{draft}/clone/{handler}",
  1439.      *     name = ContentController::ROUTES__DRAFT_CLONE,
  1440.      *     requirements = {
  1441.      *         "proxy" = "[1-9]\d*",
  1442.      *         "draft" = "[1-9]\d*",
  1443.      *         "handler" = "[a-zA-Z]+"
  1444.      *     },
  1445.      *     defaults = {
  1446.      *         "handler" = null
  1447.      *     }
  1448.      * )
  1449.      *
  1450.      * @ParamConverter(
  1451.      *     "container",
  1452.      *     converter = "cms__container"
  1453.      * )
  1454.      * @ParamConverter(
  1455.      *     "proxy",
  1456.      *     converter = "cms__module_entity"
  1457.      * )
  1458.      * @ParamConverter(
  1459.      *     "draft",
  1460.      *     converter = "cms__module_entity"
  1461.      * )
  1462.      */
  1463.     public function cloneAction(
  1464.         FeedingService $feedingService,
  1465.         Container $container,
  1466.         Proxy $proxy,
  1467.         Draft $draft null,
  1468.         ?string $handler null
  1469.     )
  1470.     {
  1471.         // ensure that all the objects are associated properly
  1472.         if ($draft === null) {
  1473.             if (($response $this->validateObjects($container$proxy)) instanceof Response) {
  1474.                 return $response;
  1475.             }
  1476.         } elseif (($response $this->validateObjects($container$proxy$draft)) instanceof Response) {
  1477.             return $response;
  1478.         }
  1479.         // make sure we are not a share
  1480.         if ($proxy instanceof ShareableInterface && ! $proxy->isMaster()) {
  1481.             throw new \Exception();
  1482.         }
  1483.         // if syncable, do not allow editing
  1484.         if ($proxy instanceof SyncableInterface && $proxy->isSynced()) {
  1485.             throw new \Exception();
  1486.         }
  1487.         // AUDIT
  1488.         if (empty($draft)) {
  1489.             $this->auditContainer($container$proxy);
  1490.         } else {
  1491.             $this->auditContainer($container$draft);
  1492.         }
  1493.         // get object
  1494.         /** @var ModuleEntity $cont */
  1495.         if (empty($draft)) {
  1496.             // do the cloning
  1497.             $content = clone $proxy;
  1498.             // if cloning a proxy, need to try to help ensure that the slug is unique
  1499.             if ($content->getData()->has('slug')) {
  1500.                 $content->getData()->set('slug'sprintf(
  1501.                     '%s-clone',
  1502.                     $content->getData()->get('slug')
  1503.                 ));
  1504.             }
  1505.             // also need to make sure that new clone does not have any history or drafts tied to it
  1506.             // TODO: should we be doing this elsewhere?
  1507.             $content
  1508.                 ->setHistory(null)
  1509.                 ->setDraft(null);
  1510.         } else {
  1511.             // do the cloning
  1512.             $content = clone $draft;
  1513.             // also need to make sure that new clone does not have any revision tied to it
  1514.             // TODO: should we be doing this elsewhere?
  1515.             $content
  1516.                 ->setRevision(null);
  1517.         }
  1518.         // fix shared stuff; cloning messes this up
  1519.         if ($content instanceof ShareableInterface) {
  1520.             $content->setSharedProxy($content);
  1521.         }
  1522.         // create the form that handles the data pieces of the module item
  1523.         $dataForm $this->createForm(
  1524.             $this->getModuleConfig()->formClass(ClassUtils::getClass($content->getData())),
  1525.             $content->getData(),
  1526.             array(
  1527.                 'container' => $container,
  1528.                 'create' => true,
  1529.                 'clone' => true,
  1530.             )
  1531.         );
  1532.         // create forms for each action that can be done once editing is finished
  1533.         /** @var array|FormInterface[] $actionForms */
  1534.         if ( ! empty($draft)) {
  1535.             $actionForms $this->getContentManager()->assembleDraftCloneActions(
  1536.                 $this->getRequest(),
  1537.                 $container,
  1538.                 $this->getGlobalContext()->getEffectiveAccount(),
  1539.                 $draft
  1540.             );
  1541.         } else {
  1542.             $actionForms $this->getContentManager()->assembleProxyCloneActions(
  1543.                 $this->getRequest(),
  1544.                 $container,
  1545.                 $this->getGlobalContext()->getEffectiveAccount(),
  1546.                 $proxy
  1547.             );
  1548.         }
  1549.         // determine which action form we are talking about, if applicable
  1550.         if ( ! empty($handler) && ! array_key_exists($handler$actionForms)) {
  1551.             throw new \Exception('There is no handler for this action.');
  1552.         }
  1553.         // handle form submission
  1554.         $valid $this->handleForm($dataForm);
  1555.         if ( ! empty($handler)) {
  1556.             $valid $this->handleForm($actionForms[$handler]['form']) && $valid;
  1557.         }
  1558.         if ($valid) {
  1559.             // get stuff from the forms
  1560.             /** @var Data $data */
  1561.             $data $dataForm->getData();
  1562.             /** @var Action $action */
  1563.             $action $actionForms[$handler]['form']->getData();
  1564.             // handle actions
  1565.             if ($content instanceof Draft) {
  1566.                 $result $this->getContentManager()->draftClone(
  1567.                     $action,
  1568.                     $content,
  1569.                     $data
  1570.                 );
  1571.             } else {
  1572.                 $result $this->getContentManager()->proxyClone(
  1573.                     $action,
  1574.                     $content,
  1575.                     $data
  1576.                 );
  1577.             }
  1578.             // LOG
  1579.             if ($result instanceof ScheduledPublication) {
  1580.                 $result $result->getContent()->getDraft();
  1581.             }
  1582.             $this->getActivityLogger()->createLog($result);
  1583.             // RECENT FEED
  1584.             $feedingService->log($result);
  1585.             return $this->redirectToRouteOrOverride(
  1586.                 ($result instanceof Draft) ? self::ROUTES__DRAFT_LIST self::ROUTES__PROXY_LIST,
  1587.                 array(
  1588.                     'container' => $container->getId(),
  1589.                     'module' => $this->getModuleConfig()->key(),
  1590.                     'proxy' => ($result instanceof Draft) ? $proxy->getId() : null,
  1591.                 )
  1592.             );
  1593.         }
  1594.         return $this->view(
  1595.             array(
  1596.                 'container' => $container,
  1597.                 'proxy' => $proxy,
  1598.                 'draft' => $draft,
  1599.                 'dataForm' => $dataForm->createView(),
  1600.                 'actionForms' => array_map(
  1601.                     function (array $data) {
  1602.                         $data['form'] = $data['form']->createView();
  1603.                         return $data;
  1604.                     },
  1605.                     $actionForms
  1606.                 ),
  1607.             )
  1608.         );
  1609.     }
  1610.     /**
  1611.      * @param Request $request
  1612.      * @param Container $container
  1613.      * @param Proxy $proxy
  1614.      * @return DocumentScene|RedirectResponse
  1615.      * @throws \Exception
  1616.      *
  1617.      * @Route(
  1618.      *     "/{proxy}/move",
  1619.      *     name = ContentController::ROUTES__PROXY_MOVE,
  1620.      *     requirements = {
  1621.      *         "proxy" = "[1-9]\d*"
  1622.      *     }
  1623.      * )
  1624.      *
  1625.      * @ParamConverter(
  1626.      *     "container",
  1627.      *     converter = "cms__container"
  1628.      * )
  1629.      * @ParamConverter(
  1630.      *     "proxy",
  1631.      *     converter = "cms__module_entity"
  1632.      * )
  1633.      */
  1634.     public function proxyMoveAction(Request $requestContainer $containerProxy $proxy)
  1635.     {
  1636.         // ensure that all the objects are associated properly
  1637.         if (($response $this->validateObjects($container$proxy)) instanceof Response) {
  1638.             return $response;
  1639.         }
  1640.         // make sure we are not a share
  1641.         if ($proxy instanceof ShareableInterface && ! $proxy->isMaster()) {
  1642.             throw new \Exception();
  1643.         }
  1644.         // AUDIT
  1645.         $this->auditContainer($container$proxy);
  1646.         // create form
  1647.         $form $this->createForm(
  1648.             MoveType::class,
  1649.             array(
  1650.                 'container' => $proxy->getContainer(),
  1651.             ),
  1652.             []
  1653.         );
  1654.         // attempt form processing
  1655.         if ($this->handleForm($form$request)) {
  1656.             // AUDIT
  1657.             // check that we have access to the target department
  1658.             try {
  1659.                 $this->auditContainer($form->getData()['container'], $proxy);
  1660.             } catch (\Exception $e) {
  1661.                 $form->addError(new FormError(
  1662.                     'You do not have permission to move content into the selected department, please choose a different department.'
  1663.                 ));
  1664.             }
  1665.             // make sure we still have no errors
  1666.             if ($form->isValid()) {
  1667.                 // TODO: need to clear out any share if one exists
  1668.                 // TODO: allow user to select in form whether or not to keep a share to the old department?
  1669.                 // TODO: do we need to restrict where it can be moved; i.e. keeping within same tree root or ensuring container types are equal?
  1670.                 // do the share via manager
  1671.                 $this->getContentManager()->proxyMove(
  1672.                     $proxy,
  1673.                     $form->getData()['container']
  1674.                 );
  1675.                 // record log
  1676.                 $this->getActivityLogger()->createLog($proxy);
  1677.                 return $this->redirectToRoute(
  1678.                     self::ROUTES__PROXY_LIST,
  1679.                     array(
  1680.                         'container' => $proxy->getContainer()->getId(),
  1681.                         'module' => $this->getModuleConfig()->key(),
  1682.                     )
  1683.                 );
  1684.             }
  1685.         }
  1686.         return $this->view(
  1687.             array(
  1688.                 'container' => $container,
  1689.                 'proxy' => $proxy,
  1690.                 'form' => $form->createView(),
  1691.             )
  1692.         );
  1693.     }
  1694.     /**
  1695.      * @param Request $request
  1696.      * @param Container $container
  1697.      * @param Proxy $proxy
  1698.      * @return DocumentScene|RedirectResponse
  1699.      * @throws \Exception
  1700.      *
  1701.      * TODO: only allow shares to departments we have access to...
  1702.      * @Route(
  1703.      *     "/{proxy}/share",
  1704.      *     name = ContentController::ROUTES__PROXY_SHARE,
  1705.      *     requirements = {
  1706.      *         "proxy" = "[1-9]\d*"
  1707.      *     }
  1708.      * )
  1709.      *
  1710.      * @ParamConverter(
  1711.      *     "container",
  1712.      *     converter = "cms__container"
  1713.      * )
  1714.      * @ParamConverter(
  1715.      *     "proxy",
  1716.      *     converter = "cms__module_entity"
  1717.      * )
  1718.      */
  1719.     public function proxyShareAction(Request $requestContainer $containerProxy $proxy)
  1720.     {
  1721.         // ensure that all the objects are associated properly
  1722.         if (($response $this->validateObjects($container$proxy)) instanceof Response) {
  1723.             return $response;
  1724.         }
  1725.         // make sure we are not already a share
  1726.         if ($proxy instanceof ShareableInterface && ! $proxy->isMaster()) {
  1727.             throw new \Exception();
  1728.         }
  1729.         // AUDIT
  1730.         $this->auditContainer($container$proxy);
  1731.         $existing array_map(
  1732.             function (Proxy $proxy) {
  1733.                 return $proxy->getContainer();
  1734.             },
  1735.             $this->getEntityManager()->createQueryBuilder()
  1736.                 ->select('shares')
  1737.                 ->from(ClassUtils::getClass($proxy), 'shares')
  1738.                 ->leftJoin('shares.container''containers')
  1739.                 ->andWhere('shares.sharedProxy = :proxy')
  1740.                 ->andWhere('shares.sharedProxy <> shares.id')
  1741.                 ->setParameter('proxy'$proxy)
  1742.                 ->getQuery()
  1743.                 ->getResult()
  1744.         );
  1745.         $form $this->createForm(
  1746.             ShareType::class,
  1747.             [
  1748.                 'containers' => $existing,
  1749.             ],
  1750.             [
  1751.                 'container' => $container,
  1752.             ]
  1753.         );
  1754.         if ($this->handleForm($form$request)) {
  1755.             $containers $form->getData()['containers'];
  1756.             if ($containers instanceof ArrayCollection) {
  1757.                 $containers $containers->toArray();
  1758.             }
  1759.             if (empty($containers)) {
  1760.                 $containers = [];
  1761.             }
  1762.             $this->getEntityManager()->transactional(
  1763.                 function (EntityManager $em) use ($proxy$containers$container) {
  1764.                     // get scalar ids
  1765.                     $ids array_map(
  1766.                         function (Container $container) {
  1767.                             return $container->getId();
  1768.                         },
  1769.                         $containers
  1770.                     );
  1771.                     // run bulk update to delete shares not needed
  1772.                     $deleted $em->createQueryBuilder()
  1773.                         ->delete(ClassUtils::getClass($proxy), 'shares')
  1774.                         ->andWhere('shares.sharedProxy = :proxy')
  1775.                         ->andWhere('shares.sharedProxy <> shares.id')
  1776.                         ->setParameter('proxy'$proxy);
  1777.                     if (count($ids) > 0) {
  1778.                         $deleted
  1779.                             ->andWhere('shares.container NOT IN (:containers)')
  1780.                             ->setParameter('containers'$ids);
  1781.                     }
  1782.                     $deleted
  1783.                         ->getQuery()
  1784.                         ->execute();
  1785.                     // determine ids we need to add
  1786.                     $existing array_map(
  1787.                         function ($value) {
  1788.                             return intval($value);
  1789.                         },
  1790.                         $em->createQueryBuilder()
  1791.                             ->select('container.id')
  1792.                             ->from(ClassUtils::getClass($proxy), 'shares')
  1793.                             ->leftJoin('shares.container''container')
  1794.                             ->andWhere('shares.sharedProxy = :proxy')
  1795.                             ->andWhere('shares.sharedProxy <> shares.id')
  1796.                             ->setParameter('proxy'$proxy)
  1797.                             ->getQuery()
  1798.                             ->execute(nullSingleColumnHydrator::HYDRATOR)
  1799.                     );
  1800.                     // create objects of ones needing to add
  1801.                     /** @var array $diffs */
  1802.                     $diffs array_diff($ids$existing);
  1803.                     foreach ($diffs as $diff) {
  1804.                         /** @var Proxy $share */
  1805.                         $share = clone ($proxy);
  1806.                         // skip ourselves
  1807.                         $cont $em->getRepository(Container::class)->findExact($diff);
  1808.                         if ($cont === $container) {
  1809.                             continue;
  1810.                         }
  1811.                         $share
  1812.                             ->setContainer($cont)
  1813.                             ->setHistory(null)
  1814.                             ->setSharedChecksum(
  1815.                                 ( ! empty($proxy->getHistory()))
  1816.                                     ? $proxy->getHistory()->getId()
  1817.                                     : null
  1818.                             )
  1819.                             ->setSharedProxy($proxy);
  1820.                         $em->persist($share);
  1821.                     }
  1822.                     // save them
  1823.                     $em->flush();
  1824.                 }
  1825.             );
  1826.             // record log
  1827.             $this->getActivityLogger()->createLog($proxy);
  1828.             // redirect to parent index
  1829.             return $this->redirectToRouteOrOverride(
  1830.                 self::ROUTES__PROXY_LIST,
  1831.                 array(
  1832.                     'container' => $proxy->getContainer()->getId(),
  1833.                     'module' => $this->getModuleConfig()->key(),
  1834.                 )
  1835.             );
  1836.         }
  1837.         return $this->view(
  1838.             array(
  1839.                 'container' => $container,
  1840.                 'proxy' => $proxy,
  1841.                 'form' => $form->createView(),
  1842.             )
  1843.         );
  1844.     }
  1845.     /**
  1846.      * @param Request $request
  1847.      * @param Container $container
  1848.      * @param Proxy $proxy
  1849.      * @return DocumentScene|AjaxScene|RedirectResponse
  1850.      * @throws \Exception
  1851.      *
  1852.      * @Route(
  1853.      *     "/{proxy}/history",
  1854.      *     name = ContentController::ROUTES__HISTORY_LIST,
  1855.      *     requirements = {
  1856.      *         "proxy" = "[1-9]\d*"
  1857.      *     }
  1858.      * )
  1859.      *
  1860.      * @ParamConverter(
  1861.      *     "proxy",
  1862.      *     converter = "cms__module_entity"
  1863.      * )
  1864.      */
  1865.     public function historyListAction(Request $requestContainer $containerProxy $proxy)
  1866.     {
  1867.         // ensure that all the objects are associated properly
  1868.         if (($response $this->validateObjects($container$proxy)) instanceof Response) {
  1869.             return $response;
  1870.         }
  1871.         // AUDIT
  1872.         $this->auditContainer($container$proxy);
  1873.         // handle searching
  1874.         $search $this->getModuleHistorySearcher()->handleRequest($request$container);
  1875.         if ($search instanceof RedirectResponse) {
  1876.             return $search;
  1877.         }
  1878.         $repo $this->getEntityManager()->getRepository($this->getModuleConfig()->classesHistory());
  1879.         if ( ! $repo instanceof SearchableInterface) {
  1880.             throw new \Exception();
  1881.         }
  1882.         $histories $repo->search($search$proxy);
  1883.         // if we are an ajax request, we should be lazyloading
  1884.         if ($request->isXmlHttpRequest()) {
  1885.             return $this->viewAjax(
  1886.                 '@CmsModule/Content/includes/histories.html.twig',
  1887.                 array(
  1888.                     'container' => $container,
  1889.                     'proxy' => $proxy,
  1890.                     'search' => $search,
  1891.                     'histories' => $histories,
  1892.                 )
  1893.             );
  1894.         }
  1895.         return $this->view(
  1896.             array(
  1897.                 'container' => $container,
  1898.                 'proxy' => $proxy,
  1899.                 'search' => $search,
  1900.                 'histories' => $histories,
  1901.             )
  1902.         );
  1903.     }
  1904.     /**
  1905.      * @param Container $container
  1906.      * @param Proxy $proxy
  1907.      * @param Draft $draft
  1908.      * @return DocumentScene|RedirectResponse
  1909.      * @throws \Exception
  1910.      *
  1911.      * @Route(
  1912.      *     "/{proxy}/draft/{draft}/publish",
  1913.      *     name = ContentController::ROUTES__DRAFT_PUBLISH,
  1914.      *     requirements = {
  1915.      *         "proxy" = "[1-9]\d*",
  1916.      *         "draft" = "[1-9]\d*"
  1917.      *     }
  1918.      * )
  1919.      *
  1920.      * @ParamConverter(
  1921.      *     "container",
  1922.      *     converter = "cms__container"
  1923.      * )
  1924.      * @ParamConverter(
  1925.      *     "proxy",
  1926.      *     converter = "cms__module_entity"
  1927.      * )
  1928.      * @ParamConverter(
  1929.      *     "draft",
  1930.      *     converter = "cms__module_entity"
  1931.      * )
  1932.      */
  1933.     public function draftPublishAction(Container $containerProxy $proxyDraft $draft)
  1934.     {
  1935.         // ensure that all the objects are associated properly
  1936.         if (($response $this->validateObjects($container$proxy$draft)) instanceof Response) {
  1937.             return $response;
  1938.         }
  1939.         // AUDIT
  1940.         $this->auditContainer($container$proxy);
  1941.         // see if we are submitted
  1942.         if ($this->handleSubmission()) {
  1943.             // use manager
  1944.             $this->getDraftManager()->draftPublish($draft);
  1945.             // record log
  1946.             $this->getActivityLogger()->createLog($draft);
  1947.             return $this->redirectToRoute(
  1948.                 self::ROUTES__DRAFT_LIST,
  1949.                 array(
  1950.                     'container' => $container->getId(),
  1951.                     'proxy' => $proxy->getId(),
  1952.                     'module' => $this->getModuleConfig()->key(),
  1953.                 )
  1954.             );
  1955.         }
  1956.         return $this->view(
  1957.             array(
  1958.                 'container' => $container,
  1959.                 'proxy' => $proxy,
  1960.                 'draft' => $draft,
  1961.             )
  1962.         );
  1963.     }
  1964.     /**
  1965.      * @param Container $container
  1966.      * @param Proxy $proxy
  1967.      * @param History $history
  1968.      * @return DocumentScene|RedirectResponse
  1969.      * @throws \Exception
  1970.      *
  1971.      * @Route(
  1972.      *     "/{proxy}/history/{history}/revert",
  1973.      *     name = ContentController::ROUTES__HISTORY_REVERT,
  1974.      *     requirements = {
  1975.      *         "proxy" = "[1-9]\d*",
  1976.      *         "history" = "[1-9]\d*"
  1977.      *     }
  1978.      * )
  1979.      *
  1980.      * @ParamConverter(
  1981.      *     "container",
  1982.      *     converter = "cms__container"
  1983.      * )
  1984.      * @ParamConverter(
  1985.      *     "proxy",
  1986.      *     converter = "cms__module_entity"
  1987.      * )
  1988.      * @ParamConverter(
  1989.      *     "history",
  1990.      *     converter = "cms__module_entity"
  1991.      * )
  1992.      */
  1993.     public function historyRevertAction(Container $containerProxy $proxyHistory $history)
  1994.     {
  1995.         // ensure that all the objects are associated properly
  1996.         if (($response $this->validateObjects($container$proxy)) instanceof Response) {
  1997.             return $response;
  1998.         }
  1999.         // AUDIT
  2000.         $this->auditContainer($container$proxy);
  2001.         // see if we are submitted
  2002.         if ($this->handleSubmission()) {
  2003.             // use manager
  2004.             $this->getContentManager()->historyRestore($history);
  2005.             // record log
  2006.             $this->getActivityLogger()->createLog($proxy);
  2007.             return $this->redirectToRoute(
  2008.                 self::ROUTES__HISTORY_LIST,
  2009.                 array(
  2010.                     'container' => $container->getId(),
  2011.                     'proxy' => $proxy->getId(),
  2012.                     'module' => $this->getModuleConfig()->key(),
  2013.                 )
  2014.             );
  2015.         }
  2016.         return $this->view(
  2017.             array(
  2018.                 'container' => $container,
  2019.                 'proxy' => $proxy,
  2020.                 'history' => $history,
  2021.             )
  2022.         );
  2023.     }
  2024.     /**
  2025.      * @param Request $request
  2026.      * @param Container $container
  2027.      * @param Proxy $proxy
  2028.      * @return DocumentScene|AjaxScene|RedirectResponse
  2029.      * @throws \Exception
  2030.      *
  2031.      * @Route(
  2032.      *     "/{proxy}/draft",
  2033.      *     name = ContentController::ROUTES__DRAFT_LIST,
  2034.      *     requirements = {
  2035.      *         "proxy" = "[1-9]\d*"
  2036.      *     }
  2037.      * )
  2038.      *
  2039.      * @ParamConverter(
  2040.      *     "container",
  2041.      *     converter = "cms__container"
  2042.      * )
  2043.      * @ParamConverter(
  2044.      *     "proxy",
  2045.      *     converter = "cms__module_entity"
  2046.      * )
  2047.      */
  2048.     public function draftListAction(Request $requestContainer $containerProxy $proxy)
  2049.     {
  2050.         // ensure that all the objects are associated properly
  2051.         if (($response $this->validateObjects($container$proxy)) instanceof Response) {
  2052.             return $response;
  2053.         }
  2054.         // AUDIT
  2055.         $this->auditContainer($container$proxy);
  2056.         // handle searching
  2057.         $search $this->getModuleDraftSearcher()->handleRequest($request$container);
  2058.         if ($search instanceof RedirectResponse) {
  2059.             return $search;
  2060.         }
  2061.         $repo $this->getEntityManager()->getRepository($this->getModuleConfig()->classesDraft());
  2062.         if ( ! $repo instanceof SearchableInterface) {
  2063.             throw new \Exception();
  2064.         }
  2065.         $drafts $repo->search($search$proxy);
  2066.         // if we are an ajax request, we should be lazyloading
  2067.         if ($request->isXmlHttpRequest()) {
  2068.             return $this->viewAjax(
  2069.                 '@CmsModule/Content/includes/drafts.html.twig',
  2070.                 array(
  2071.                     'container' => $container,
  2072.                     'proxy' => $proxy,
  2073.                     'search' => $search,
  2074.                     'drafts' => $drafts,
  2075.                 )
  2076.             );
  2077.         }
  2078.         return $this->view(
  2079.             array(
  2080.                 'container' => $container,
  2081.                 'proxy' => $proxy,
  2082.                 'search' => $search,
  2083.                 'drafts' => $drafts,
  2084.             )
  2085.         );
  2086.     }
  2087.     /**
  2088.      * @param Request $request
  2089.      * @param Container $container
  2090.      * @param Proxy $proxy
  2091.      * @param Draft $draft
  2092.      * @return DocumentScene|AjaxScene|RedirectResponse
  2093.      * @throws \Exception
  2094.      *
  2095.      * @Route(
  2096.      *     "/{proxy}/draft/{draft}/revision",
  2097.      *     name = ContentController::ROUTES__REVISION_LIST,
  2098.      *     requirements = {
  2099.      *         "proxy" = "[1-9]\d*",
  2100.      *         "draft" = "[1-9]\d*"
  2101.      *     }
  2102.      * )
  2103.      *
  2104.      * @ParamConverter(
  2105.      *     "container",
  2106.      *     converter = "cms__container"
  2107.      * )
  2108.      * @ParamConverter(
  2109.      *     "proxy",
  2110.      *     converter = "cms__module_entity"
  2111.      * )
  2112.      * @ParamConverter(
  2113.      *     "draft",
  2114.      *     converter = "cms__module_entity"
  2115.      * )
  2116.      */
  2117.     public function revisionListAction(Request $requestContainer $containerProxy $proxyDraft $draft)
  2118.     {
  2119.         // ensure that all the objects are associated properly
  2120.         if (($response $this->validateObjects($container$proxy$draft)) instanceof Response) {
  2121.             return $response;
  2122.         }
  2123.         // AUDIT
  2124.         $this->auditContainer($container$draft);
  2125.         // handle searching
  2126.         $search $this->getModuleRevisionSearcher()->handleRequest($request$container);
  2127.         if ($search instanceof RedirectResponse) {
  2128.             return $search;
  2129.         }
  2130.         $repo $this->getEntityManager()->getRepository($this->getModuleConfig()->classesRevision());
  2131.         if ( ! $repo instanceof SearchableInterface) {
  2132.             throw new \Exception();
  2133.         }
  2134.         $revisions $repo->search($search$draft);
  2135.         // if we are an ajax request, we should be lazyloading
  2136.         if ($request->isXmlHttpRequest()) {
  2137.             return $this->viewAjax(
  2138.                 '@CmsModule/Content/includes/revisions.html.twig',
  2139.                 array(
  2140.                     'container' => $container,
  2141.                     'proxy' => $proxy,
  2142.                     'draft' => $draft,
  2143.                     'search' => $search,
  2144.                     'revisions' => $revisions,
  2145.                 )
  2146.             );
  2147.         }
  2148.         return $this->view(
  2149.             array(
  2150.                 'container' => $container,
  2151.                 'proxy' => $proxy,
  2152.                 'draft' => $draft,
  2153.                 'search' => $search,
  2154.                 'revisions' => $revisions,
  2155.             )
  2156.         );
  2157.     }
  2158.     /**
  2159.      * @param Container $container
  2160.      * @param Proxy $proxy
  2161.      * @param Draft|null $draft
  2162.      * @param Revision|null $revision
  2163.      * @return RedirectResponse|null
  2164.      */
  2165.     private function validateObjects(Container $containerProxy $proxyDraft $draft nullRevision $revision null): ?RedirectResponse
  2166.     {
  2167.         $redirectRoute $this->redirectToRoute(
  2168.             self::ROUTES__PROXY_LIST,
  2169.             [
  2170.                 'container' => $proxy->getContainer()->getId(),
  2171.                 'module' => $this->getModuleConfig()->key(),
  2172.             ]
  2173.         );
  2174.         // check proxy and container
  2175.         if ($proxy->getContainer()->getId() !== $container->getId()) {
  2176.             return $redirectRoute;
  2177.         }
  2178.         // handle draft if given
  2179.         if ( ! empty($draft) || ! empty($revision)) {
  2180.             if ($draft->getProxy()->getId() !== $proxy->getId()) {
  2181.                 return $redirectRoute;
  2182.             }
  2183.         }
  2184.         // handle revision if given
  2185.         if ( ! empty($revision)) {
  2186.             if ($revision->getDraft()->getId() !== $draft->getId()) {
  2187.                 return $redirectRoute;
  2188.             }
  2189.         }
  2190.         return null;
  2191.     }
  2192.     /**
  2193.      * @param Container $container
  2194.      * @param Proxy $proxy
  2195.      * @param Draft $draft
  2196.      * @param Revision $revision
  2197.      * @return DocumentScene|RedirectResponse
  2198.      * @throws \Exception
  2199.      *
  2200.      * @Route(
  2201.      *     "/{proxy}/draft/{draft}/revision/{revision}/revert",
  2202.      *     name = ContentController::ROUTES__REVISION_REVERT,
  2203.      *     requirements = {
  2204.      *         "proxy" = "[1-9]\d*",
  2205.      *         "draft" = "[1-9]\d*",
  2206.      *         "revision" = "[1-9]\d*"
  2207.      *     }
  2208.      * )
  2209.      *
  2210.      * @ParamConverter(
  2211.      *     "container",
  2212.      *     converter = "cms__container"
  2213.      * )
  2214.      * @ParamConverter(
  2215.      *     "proxy",
  2216.      *     converter = "cms__module_entity"
  2217.      * )
  2218.      * @ParamConverter(
  2219.      *     "draft",
  2220.      *     converter = "cms__module_entity"
  2221.      * )
  2222.      * @ParamConverter(
  2223.      *     "revision",
  2224.      *     converter = "cms__module_entity"
  2225.      * )
  2226.      */
  2227.     public function revisionRevertAction(Container $containerProxy $proxyDraft $draftRevision $revision)
  2228.     {
  2229.         // ensure that all the objects are associated properly
  2230.         if (($response $this->validateObjects($container$proxy$draft$revision)) instanceof Response) {
  2231.             return $response;
  2232.         }
  2233.         // AUDIT
  2234.         $this->auditContainer($container$draft);
  2235.         // see if we are submitted
  2236.         if ($this->handleSubmission()) {
  2237.             // use manager
  2238.             $this->getContentManager()->revisionRestore($revision);
  2239.             // record log
  2240.             $this->getActivityLogger()->createLog($draft);
  2241.             return $this->redirectToRoute(
  2242.                 self::ROUTES__DRAFT_LIST,
  2243.                 array(
  2244.                     'container' => $container->getId(),
  2245.                     'proxy' => $proxy->getId(),
  2246.                     'module' => $this->getModuleConfig()->key(),
  2247.                 )
  2248.             );
  2249.         }
  2250.         return $this->view(
  2251.             array(
  2252.                 'container' => $container,
  2253.                 'proxy' => $proxy,
  2254.                 'draft' => $draft,
  2255.                 'revision' => $revision,
  2256.             )
  2257.         );
  2258.     }
  2259.     /**
  2260.      * @param Container $container
  2261.      * @param Proxy $proxy
  2262.      * @return DocumentScene|RedirectResponse
  2263.      * @throws \Exception
  2264.      *
  2265.      * @Route(
  2266.      *     "/{proxy}/delete",
  2267.      *     name = ContentController::ROUTES__PROXY_DELETE,
  2268.      *     requirements = {
  2269.      *         "proxy" = "[1-9]\d*"
  2270.      *     }
  2271.      * )
  2272.      *
  2273.      * @ParamConverter(
  2274.      *     "container",
  2275.      *     converter = "cms__container"
  2276.      * )
  2277.      * @ParamConverter(
  2278.      *     "proxy",
  2279.      *     converter = "cms__module_entity"
  2280.      * )
  2281.      */
  2282.     public function proxyDeleteAction(Container $containerProxy $proxy)
  2283.     {
  2284.         // ensure that all the objects are associated properly
  2285.         if (($response $this->validateObjects($container$proxy)) instanceof Response) {
  2286.             return $response;
  2287.         }
  2288.         // AUDIT
  2289.         $this->auditContainer($container$proxy);
  2290.         // process submission
  2291.         if ($this->handleSubmission()) {
  2292.             // run it through the content manager
  2293.             $id $this->getContentManager()->proxyDelete($proxy);
  2294.             // record log
  2295.             $this->getActivityLogger()->createLog($proxy$id);
  2296.             return $this->redirectToRoute(
  2297.                 self::ROUTES__PROXY_LIST,
  2298.                 array(
  2299.                     'container' => $container->getId(),
  2300.                     'module' => $this->getModuleConfig()->key(),
  2301.                 )
  2302.             );
  2303.         }
  2304.         return $this->view(
  2305.             array(
  2306.                 'container' => $container,
  2307.                 'proxy' => $proxy,
  2308.             )
  2309.         );
  2310.     }
  2311.     /**
  2312.      * @param Container $container
  2313.      * @param Proxy $proxy
  2314.      * @param Draft $draft
  2315.      * @return DocumentScene|RedirectResponse
  2316.      * @throws \Exception
  2317.      *
  2318.      * @Route(
  2319.      *     "/{proxy}/draft/{draft}/delete",
  2320.      *     name = ContentController::ROUTES__DRAFT_DELETE,
  2321.      *     requirements = {
  2322.      *         "proxy" = "[1-9]\d*",
  2323.      *         "draft" = "[1-9]\d*"
  2324.      *     }
  2325.      * )
  2326.      *
  2327.      * @ParamConverter(
  2328.      *     "container",
  2329.      *     converter = "cms__container"
  2330.      * )
  2331.      * @ParamConverter(
  2332.      *     "proxy",
  2333.      *     converter = "cms__module_entity"
  2334.      * )
  2335.      * @ParamConverter(
  2336.      *     "draft",
  2337.      *     converter = "cms__module_entity"
  2338.      * )
  2339.      */
  2340.     public function draftDeleteAction(Container $containerProxy $proxyDraft $draft)
  2341.     {
  2342.         // ensure that all the objects are associated properly
  2343.         if (($response $this->validateObjects($container$proxy$draft)) instanceof Response) {
  2344.             return $response;
  2345.         }
  2346.         // AUDIT
  2347.         $this->auditContainer($container$draft);
  2348.         // process submission
  2349.         if ($this->handleSubmission()) {
  2350.             // run it through the manager
  2351.             $id $this->getContentManager()->draftDelete($draft);
  2352.             // record log
  2353.             $this->getActivityLogger()->createLog($draft$id);
  2354.             return $this->redirectToRoute(
  2355.                 self::ROUTES__DRAFT_LIST,
  2356.                 array(
  2357.                     'container' => $container->getId(),
  2358.                     'module' => $this->getModuleConfig()->key(),
  2359.                     'proxy' => $proxy->getId(),
  2360.                 )
  2361.             );
  2362.         }
  2363.         return $this->view(
  2364.             array(
  2365.                 'container' => $container,
  2366.                 'proxy' => $proxy,
  2367.                 'draft' => $draft,
  2368.             )
  2369.         );
  2370.     }
  2371.     /**
  2372.      * @param Container $container
  2373.      * @return ModuleSettings
  2374.      */
  2375.     private function loadSettings(Container $container)
  2376.     {
  2377.         // generate the class name for the settings
  2378.         $class $this->getModuleConfig()->settingsClass();
  2379.         // obtain the repository
  2380.         /** @var ModuleSettingsRepository $repo */
  2381.         $repo $this->getEntityManager()->getRepository($class);
  2382.         // try to find one given the container
  2383.         $settings $repo->findOneByContainer($container);
  2384.         // if none, make a new copy, link up to container
  2385.         if (empty($settings)) {
  2386.             /** @var ModuleSettings $settings */
  2387.             $settings = new $class();
  2388.             $settings->setContainer($container);
  2389.         }
  2390.         // done
  2391.         return $settings;
  2392.     }
  2393.     /**
  2394.      * @param Container $container
  2395.      * @return DocumentScene|RedirectResponse
  2396.      *
  2397.      * @Route(
  2398.      *     "/settings",
  2399.      *     name = ContentController::ROUTES__SETTINGS
  2400.      * )
  2401.      */
  2402.     public function settingsAction(Container $container)
  2403.     {
  2404.         // AUDIT
  2405.         $this->auditContainer($container);
  2406.         // find settings or make a new one if none
  2407.         $settings $this->loadSettings($container);
  2408.         // create form
  2409.         $class $this->getModuleConfig()->settingsForm();
  2410.         $form $this->createForm($class$settings);
  2411.         // handle form if needed
  2412.         if ($this->handleForm($form)) {
  2413.             // save settings
  2414.             $this->getEntityManager()->save($settings);
  2415.             // record log
  2416.             $this->getActivityLogger()->createLog($settings);
  2417.             // back to viewing list of module data
  2418.             return $this->redirectToRoute(self::ROUTES__PROXY_LIST, array(
  2419.                 'container' => $container->getId(),
  2420.                 'module' => $this->getModuleConfig()->key(),
  2421.             ));
  2422.         }
  2423.         return $this->view(
  2424.             array(
  2425.                 'container' => $container,
  2426.                 'form' => $form->createView(),
  2427.             )
  2428.         );
  2429.     }
  2430.     /**
  2431.      * @return ContentManager|object
  2432.      */
  2433.     private function getContentManager(): ContentManager
  2434.     {
  2435.         return $this->get(__METHOD__);
  2436.     }
  2437.     /**
  2438.      * @return ModuleManager|object
  2439.      */
  2440.     private function getModuleManager(): ModuleManager
  2441.     {
  2442.         return $this->get(__METHOD__);
  2443.     }
  2444.     /**
  2445.      * @return Slugger|object
  2446.      */
  2447.     private function getSlugger(): Slugger
  2448.     {
  2449.         return $this->get(__METHOD__);
  2450.     }
  2451.     /**
  2452.      * @return Transcoder|object
  2453.      */
  2454.     private function getTranscoder(): Transcoder
  2455.     {
  2456.         return $this->get(__METHOD__);
  2457.     }
  2458.     /**
  2459.      * @return PublicationService|object
  2460.      */
  2461.     private function getPublicationService(): PublicationService
  2462.     {
  2463.         return $this->get(__METHOD__);
  2464.     }
  2465.     /**
  2466.      * @return DraftManager|object
  2467.      */
  2468.     private function getDraftManager(): DraftManager
  2469.     {
  2470.         return $this->get(__METHOD__);
  2471.     }
  2472.     /**
  2473.      * @return WorkflowsManager|object
  2474.      */
  2475.     private function getWorkflowsManager(): WorkflowsManager
  2476.     {
  2477.         return $this->get(__METHOD__);
  2478.     }
  2479.     /**
  2480.      * @return Publisher|object
  2481.      */
  2482.     private function getPublisher(): Publisher
  2483.     {
  2484.         return $this->get(__METHOD__);
  2485.     }
  2486.     /**
  2487.      * @return ModuleHistorySearcher|object
  2488.      */
  2489.     private function getModuleHistorySearcher(): ModuleHistorySearcher
  2490.     {
  2491.         return $this->get(__METHOD__);
  2492.     }
  2493.     /**
  2494.      * @return ModuleDraftSearcher|object
  2495.      */
  2496.     private function getModuleDraftSearcher(): ModuleDraftSearcher
  2497.     {
  2498.         return $this->get(__METHOD__);
  2499.     }
  2500.     /**
  2501.      * @return ModuleRevisionSearcher|object
  2502.      */
  2503.     private function getModuleRevisionSearcher(): ModuleRevisionSearcher
  2504.     {
  2505.         return $this->get(__METHOD__);
  2506.     }
  2507. }