src/Platform/ControlPanelBundle/Controller/Dashboard/TenantController.php line 67

Open in your IDE?
  1. <?php
  2. namespace Platform\ControlPanelBundle\Controller\Dashboard;
  3. use App\Entity\System\Sendgrid\SendgridConfig;
  4. use App\Service\Messaging\MessagingContext;
  5. use Cms\CoreBundle\Entity\OneRosterJob;
  6. use Cms\CoreBundle\Entity\OneRosterSync;
  7. use Cms\CoreBundle\Model\Contexts\GlobalContext;
  8. use Cms\CoreBundle\Model\Scenes\DashboardScenes\DocumentScene;
  9. use Cms\CoreBundle\Model\Search\Pagination;
  10. use Cms\CoreBundle\Model\Search\Search;
  11. use Cms\CoreBundle\Service\OneRoster\AbstractOneRosterApi;
  12. use Cms\CoreBundle\Service\OneRosterService;
  13. use Cms\TenantBundle\Doctrine\TenantRepository;
  14. use Cms\TenantBundle\Entity\Tenant;
  15. use Cms\TenantBundle\Service\Search\TenantSearcher;
  16. use Cms\TenantBundle\Service\TenantService;
  17. use Doctrine\ORM\QueryBuilder;
  18. use Platform\ControlPanelBundle\Controller\DashboardController;
  19. use Platform\ControlPanelBundle\Form\OneRosterSyncType;
  20. use Platform\ControlPanelBundle\Form\TenantEditType;
  21. use Platform\ControlPanelBundle\Form\TenantType;
  22. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  23. use Symfony\Component\Routing\Annotation\Route;
  24. use Symfony\Component\Form\Extension\Core\Type\IntegerType;
  25. use Symfony\Component\Form\Extension\Core\Type\TextType;
  26. use Symfony\Component\HttpFoundation\RedirectResponse;
  27. use Symfony\Component\HttpFoundation\Request;
  28. /**
  29.  * Class ControlPanelController
  30.  * @package Platform\ControlPanelBundle\Dashboard\Controller
  31.  */
  32. final class TenantController extends DashboardController
  33. {
  34.     public const ROUTES__INDEX 'platform.control_panel.dashboard.tenant.index';
  35.     public const ROUTES__LIST 'platform.control_panel.dashboard.tenant.list';
  36.     public const ROUTES__CREATE 'platform.control_panel.dashboard.tenant.create';
  37.     public const ROUTES__EDIT 'platform.control_panel.dashboard.tenant.edit';
  38.     public const ROUTES__ONE_ROSTER_LANDING 'platform.control_panel.dashboard.tenant.one_roster_landing';
  39.     public const ROUTES__ONE_ROSTER_SETUP 'platform.control_panel.dashboard.tenant.one_roster_setup';
  40.     public const ROUTES__ONE_ROSTER_EXPLORER 'platform.control_panel.dashboard.tenant.one_roster_explorer';
  41.     public const ROUTES__ONE_ROSTER_DESTROY 'platform.control_panel.dashboard.tenant.one_roster_destroy';
  42.     public const ROUTES__ONE_ROSTER_ACTION 'platform.control_panel.dashboard.tenant.one_roster_action';
  43.     public const ROUTES__NOTIFICATIONS_ONBOARDING 'platform.control_panel.dashboard.tenant.notifications_onboarding';
  44.     public const ROUTES__PHPINFO 'platform.control_panel.dashboard.tenant.phpinfo';
  45.     public const ROUTES__EXCP 'platform.control_panel.dashboard.tenant.excp';
  46.     public const ROUTES__ONE_ROSTER_SYNCS 'platform.control_panel.dashboard.tenant.one_roster_syncs';
  47.     public const ROUTES__ONE_ROSTER_SYNCS_ACTION 'platform.control_panel.dashboard.tenant.one_roster_syncs_action';
  48.     public const ROUTES__ONE_ROSTER_DELETE 'platform.control_panel.dashboard.tenant.one_roster_delete';
  49.     public const ROUTES__ONE_ROSTER_TOGGLE_SYNC 'platform.control_panel.dashboard.tenant.one_roster_toggle_sync';
  50.     /**
  51.      * @return TenantRepository
  52.      */
  53.     private function getTenantRepository()
  54.     {
  55.         return $this->getEntityManager()->getRepository(Tenant::class);
  56.     }
  57.     /**
  58.      * @return TenantService|object
  59.      */
  60.     private function getTenantService(): TenantService
  61.     {
  62.         return $this->get(__METHOD__);
  63.     }
  64.     /**
  65.      * @return OneRosterService|object
  66.      */
  67.     private function getOneRosterService(): OneRosterService
  68.     {
  69.         return $this->get(__METHOD__);
  70.     }
  71.     /**
  72.      * @return TenantSearcher|object
  73.      */
  74.     private function getTenantSearcher(): TenantSearcher
  75.     {
  76.         return $this->get(__METHOD__);
  77.     }
  78.     /**
  79.      * @return MessagingContext|object
  80.      */
  81.     private function getMessagingContext(): MessagingContext
  82.     {
  83.         return $this->get(__METHOD__);
  84.     }
  85.     /**
  86.      * @Route(
  87.      *  "/phpinfo",
  88.      *  name = TenantController::ROUTES__PHPINFO
  89.      * )
  90.      */
  91.     public function phpinfoAction()
  92.     {
  93.         phpinfo();exit;
  94.     }
  95.     /**
  96.      * @Route(
  97.      *  "/excp",
  98.      *  name = TenantController::ROUTES__EXCP
  99.      * )
  100.      */
  101.     public function excpAction()
  102.     {
  103.         throw new \Exception('Rollbar test.');
  104.     }
  105.     /**
  106.      * @return RedirectResponse
  107.      *
  108.      * @Route(
  109.      *     "",
  110.      *     name = TenantController::ROUTES__INDEX
  111.      * )
  112.      */
  113.     public function indexAction()
  114.     {
  115.         return $this->redirectToRoute(self::ROUTES__LIST);
  116.     }
  117.     /**
  118.      * @return DocumentScene
  119.      *
  120.      * @Route(
  121.      *     "/oneroster",
  122.      *     name = TenantController::ROUTES__ONE_ROSTER_SYNCS
  123.      * )
  124.      */
  125.     public function oneRosterSyncsAction()
  126.     {
  127.         ini_set('memory_limit','256M');
  128.         $syncs $this->getContextManager()->getGlobalContext()->transactional(
  129.             function (GlobalContext $gc) {
  130.                 $gc->setTenant(null);
  131.                 return $this->getEntityManager()->getRepository(OneRosterSync::class)
  132.                     ->createQueryBuilder('syncs')
  133.                     ->leftJoin('syncs.tenant''tenant')
  134.                     ->addOrderBy('tenant.name''ASC')
  135.                     ->getQuery()
  136.                     ->getResult();
  137.             }
  138.         );
  139.         return $this->view([
  140.             'syncs' => $syncs
  141.         ]);
  142.     }
  143.     /**
  144.      * @param string $action
  145.      * @return RedirectResponse
  146.      *
  147.      * @Route(
  148.      *     "/oneroster/action/{action}",
  149.      *     name = TenantController::ROUTES__ONE_ROSTER_SYNCS_ACTION
  150.      * )
  151.      */
  152.     public function onerosterSyncsActionAction($action)
  153.     {
  154.         ini_set('memory_limit','256M');
  155.         $syncs $this->getContextManager()->getGlobalContext()->transactional(
  156.             function (GlobalContext $gc) {
  157.                 $gc->setTenant(null);
  158.                 return $this->getEntityManager()->getRepository(OneRosterSync::class)->findRunnable();
  159.             }
  160.         );
  161.         foreach ($syncs as $sync) {
  162.             // TODO: check that the sync isn't currently running
  163.             $this->getContextManager()->getGlobalContext()->transactional(
  164.                 function (GlobalContext $gc) use ($sync$action) {
  165.                     $gc->setTenant($sync->getTenant());
  166.                     $this->getOneRosterService()->queue(
  167.                         $sync,
  168.                         OneRosterJob::PHASES[$action]
  169.                     );
  170.                 }
  171.             );
  172.         }
  173.         return $this->redirectToRoute(self::ROUTES__ONE_ROSTER_SYNCS);
  174.     }
  175.     /**
  176.      * @param Request $request
  177.      * @param string|null $primary
  178.      * @param string|null $secondary
  179.      * @return DocumentScene
  180.      *
  181.      * @Route(
  182.      *     "/tenants/list",
  183.      *     name = TenantController::ROUTES__LIST
  184.      * )
  185.      * @Route(
  186.      *     "/tenants/list/{primary}",
  187.      *     name = TenantController::ROUTES__LIST
  188.      * )
  189.      * @Route(
  190.      *     "/tenants/list/{primary}/{secondary}",
  191.      *     name = TenantController::ROUTES__LIST
  192.      * )
  193.      */
  194.     public function listAction(Request $request$primary null$secondary null)
  195.     {
  196.         $search $this->getTenantSearcher()->handleRequest($request);
  197.         if ($search instanceof RedirectResponse) {
  198.             return $search;
  199.         }
  200.         // TODO: this is a hack for now...
  201.         $search->addPagination(new Pagination('pagination:1x1000'));
  202.         $search->registerPreFilter(function (QueryBuilder $qbSearch $search) use ($primary$secondary) {
  203.             if ($primary === 'pending-removal') {
  204.                 $qb
  205.                     ->andWhere('nodes.status = :status')
  206.                     ->setParameter('status'Tenant::STATUS__INACTIVE);
  207.             } else {
  208.                 $qb
  209.                     ->andWhere('nodes.status IN (:statuses)')
  210.                     ->setParameter('statuses', [
  211.                         Tenant::STATUS__OK,
  212.                         Tenant::STATUS__SUSPENDED,
  213.                     ]);
  214.                 if ( ! empty($primary)) {
  215.                     $qb
  216.                         ->andWhere('nodes.type.primary = :primary')
  217.                         ->setParameter('primary'$primary);
  218.                     if ( ! empty($secondary)) {
  219.                         $qb
  220.                             ->andWhere('nodes.type.secondary = :secondary')
  221.                             ->setParameter('secondary'$secondary);
  222.                     }
  223.                 }
  224.             }
  225.         });
  226.         return $this->view(array(
  227.             'domain' => $this->getParameter('dashboard.hostname'),
  228.             'tenants' => $this->getTenantRepository()->search($search),
  229.             'current' => $this->getContextManager()->getGlobalContext()->getTenant(),
  230.             'search' => $search,
  231.         ));
  232.     }
  233.     /**
  234.      * @param Request $request
  235.      * @return DocumentScene|RedirectResponse
  236.      * @throws \Exception
  237.      *
  238.      * @Route(
  239.      *  "/tenants/create",
  240.      *  name = TenantController::ROUTES__CREATE
  241.      * )
  242.      */
  243.     public function createAction(Request $request)
  244.     {
  245.         //tenant creation form
  246.         $form $this->createForm(TenantType::class);
  247.         if ($this->handleForm($form$request)) {
  248.             //create new tenant
  249.             try {
  250.                 $this
  251.                     ->getTenantService()
  252.                     ->createTenant(
  253.                         $form->get('name')->getData(),
  254.                         $form->get('slug')->getData(),
  255.                         $this->getParameter('app.security.csadmin.username'),
  256.                         $this->getParameter('app.security.csadmin.password'),
  257.                         $form->get('teamworkTasklist')->getData()
  258.                     );
  259.             } catch (\Exception $e) {
  260.                 $this->addFlash('error'$e->getMessage());
  261.                 return $this->view(array(
  262.                     'form' => $form->createView()
  263.                 ));
  264.             }
  265.             return $this->redirectToRoute(self::ROUTES__INDEX);
  266.         }
  267.         return $this->view(array(
  268.             'form' => $form->createView()
  269.         ));
  270.     }
  271.     /**
  272.      * @param Request $request
  273.      * @param int $tenantId
  274.      * @return DocumentScene|RedirectResponse
  275.      * @throws \Exception
  276.      *
  277.      * @Route(
  278.      *      "/tenants/{tenantId}/edit",
  279.      *      name = TenantController::ROUTES__EDIT,
  280.      *      requirements = {
  281.      *          "tenantId" = "[0-9]+"
  282.      *      }
  283.      * )
  284.      */
  285.     public function editAction(Request $request$tenantId)
  286.     {
  287.         $tenant $this->getTenantRepository()->findExact($tenantId);
  288.         $form $this->createForm(TenantEditType::class, $tenant, []);
  289.         if ($this->handleForm($form$request)) {
  290.             // save tenant
  291.             $this->getEntityManager()->save($tenant);
  292.             // redirect to tenant list
  293.             return $this->redirectToRoute(self::ROUTES__INDEX);
  294.         }
  295.         return $this->view(array(
  296.             'tenant' => $tenant,
  297.             'form' => $form->createView(),
  298.         ));
  299.     }
  300.     /**
  301.      * @param Tenant $tenant
  302.      * @return DocumentScene
  303.      *
  304.      * @Route(
  305.      *     "/tenants/{tenant}/oneroster",
  306.      *     name = TenantController::ROUTES__ONE_ROSTER_LANDING
  307.      * )
  308.      *
  309.      * @ParamConverter(
  310.      *     "tenant",
  311.      *     class = Tenant::class,
  312.      * )
  313.      */
  314.     public function onerosterLandingAction(Tenant $tenant)
  315.     {
  316.         $this->getContextManager()->getGlobalContext()->unlock()->setTenant($tenant)->lock();
  317.         return $this->view(
  318.             array(
  319.                 'tenant' => $tenant,
  320.                 'sync' => $this->getEntityManager()->getRepository(OneRosterSync::class)->findOneBy(array(
  321.                     'tenant' => $tenant,
  322.                 )),
  323.             )
  324.         );
  325.     }
  326.     /**
  327.      * @param Tenant $tenant
  328.      * @return DocumentScene|RedirectResponse
  329.      *
  330.      * @Route(
  331.      *     "/tenants/{tenant}/oneroster/setup",
  332.      *     name = TenantController::ROUTES__ONE_ROSTER_SETUP
  333.      * )
  334.      *
  335.      * @ParamConverter(
  336.      *     "tenant",
  337.      *     class = Tenant::class,
  338.      * )
  339.      */
  340.     public function onerosterSetupAction(Tenant $tenant)
  341.     {
  342.         $this->getContextManager()->getGlobalContext()->unlock()->setTenant($tenant)->lock();
  343.         $sync $this->getEntityManager()->getRepository(OneRosterSync::class)->findOneBy(array(
  344.             'tenant' => $tenant,
  345.         ));
  346.         if (empty($sync)) {
  347.             $sync = new OneRosterSync();
  348.         }
  349.         $form $this->createForm(
  350.             OneRosterSyncType::class,
  351.             $sync,
  352.             []
  353.         );
  354.         if ($this->handleForm($form)) {
  355.             $this->getContextManager()->getGlobalContext()->transactional(
  356.                 function (GlobalContext $gc) use ($tenant$sync) {
  357.                     $gc->setTenant($tenant);
  358.                     $this->getEntityManager()->save($sync);
  359.                 }
  360.             );
  361.             return $this->redirectToRoute(
  362.                 self::ROUTES__ONE_ROSTER_LANDING,
  363.                 array(
  364.                     'tenant' => $tenant->getId(),
  365.                 )
  366.             );
  367.         }
  368.         return $this->view(
  369.             array(
  370.                 'tenant' => $tenant,
  371.                 'sync' => $sync,
  372.                 'form' => $form->createView(),
  373.             )
  374.         );
  375.     }
  376.     /**
  377.      * @param Tenant $tenant
  378.      * @return DocumentScene|RedirectResponse
  379.      *
  380.      * @Route(
  381.      *     "/tenants/{tenant}/oneroster/explorer",
  382.      *     name = TenantController::ROUTES__ONE_ROSTER_EXPLORER,
  383.      * )
  384.      *
  385.      * @ParamConverter(
  386.      *     "tenant",
  387.      *     class = Tenant::class,
  388.      * )
  389.      */
  390.     public function onerosterExplorerAction(Tenant $tenant)
  391.     {
  392.         $this->getContextManager()->getGlobalContext()->unlock()->setTenant($tenant)->lock();
  393.         $sync $this->getEntityManager()->getRepository(OneRosterSync::class)->findOneBy(array(
  394.             'tenant' => $tenant,
  395.         ));
  396.         if (empty($sync)) {
  397.             return $this->redirectToRoute(self::ROUTES__ONE_ROSTER_LANDING, [
  398.                 'tenant' => $tenant->getId(),
  399.             ]);
  400.         }
  401.         $endpoint $this->getRequest()->query->get('endpoint');
  402.         $form null;
  403.         $vars = [];
  404.         if ($endpoint) {
  405.             $form $this->createFormBuilder([], [
  406.                 'action' => $this->generateUrl(self::ROUTES__ONE_ROSTER_EXPLORER, [
  407.                     'tenant' => $tenant->getId(),
  408.                     'endpoint' => $endpoint,
  409.                 ]),
  410.             ]);
  411.             preg_match_all('/{(.+?)}/'$endpoint$vars);
  412.             foreach ($vars[1] as $var) {
  413.                 $form->add($varTextType::class, []);
  414.             }
  415.             if (AbstractOneRosterApi::ENDPOINTS[$endpoint] === true) {
  416.                 $form
  417.                     ->add('offset'IntegerType::class, [
  418.                         'required' => false,
  419.                     ])
  420.                     ->add('limit'IntegerType::class, [
  421.                         'required' => false,
  422.                     ]);
  423.             }
  424.             $form $form->getForm();
  425.         } else {
  426.             $this->getEntityManager()->save(
  427.                 $sync->setApiToken(
  428.                     $this->getOneRosterService()->api($sync)->token(true)
  429.                 )
  430.             );
  431.         }
  432.         $result null;
  433.         if ($endpoint && $form && $this->handleForm($form)) {
  434.             $enpt $endpoint;
  435.             foreach ($vars[1] as $var) {
  436.                 $enpt str_replace(
  437.                     sprintf(
  438.                         '{%s}',
  439.                         $var
  440.                     ),
  441.                     $form->get($var)->getData(),
  442.                     $enpt
  443.                 );
  444.             }
  445.             $result $this->getOneRosterService()->api($sync)->raw(
  446.                 $enpt,
  447.                 array_diff_key($form->getData(), $vars[1])
  448.             );
  449.         }
  450.         return $this->view(
  451.             array(
  452.                 'tenant' => $tenant,
  453.                 'selection' => $endpoint,
  454.                 'endpoints' => array_keys(AbstractOneRosterApi::ENDPOINTS),
  455.                 'form' => ($form) ? $form->createView() : null,
  456.                 'result' => $result,
  457.             )
  458.         );
  459.     }
  460.     /**
  461.      * @param Tenant $tenant
  462.      * @return DocumentScene
  463.      *
  464.      * @Route(
  465.      *     "/tenants/{tenant}/oneroster/destroy",
  466.      *     name = TenantController::ROUTES__ONE_ROSTER_DESTROY
  467.      * )
  468.      *
  469.      * @ParamConverter(
  470.      *     "tenant",
  471.      *     class = Tenant::class,
  472.      * )
  473.      */
  474.     public function onerosterDestroyAction(Tenant $tenant)
  475.     {
  476.         $this->getContextManager()->getGlobalContext()->unlock()->setTenant($tenant)->lock();
  477.         return $this->view(
  478.             array(
  479.                 'tenant' => $tenant,
  480.                 'sync' => $this->getEntityManager()->getRepository(OneRosterSync::class)->findOneBy(array(
  481.                     'tenant' => $tenant,
  482.                 )),
  483.             )
  484.         );
  485.     }
  486.     /**
  487.      * @param Tenant $tenant
  488.      * @param string $action
  489.      * @return RedirectResponse
  490.      *
  491.      * @Route(
  492.      *     "/tenants/{tenant}/oneroster/action/{action}",
  493.      *     name = TenantController::ROUTES__ONE_ROSTER_ACTION
  494.      * )
  495.      *
  496.      * @ParamConverter(
  497.      *     "tenant",
  498.      *     class = Tenant::class,
  499.      * )
  500.      */
  501.     public function onerosterActionAction(Tenant $tenant$action)
  502.     {
  503.         $this->getContextManager()->getGlobalContext()->unlock()->setTenant($tenant)->lock();
  504.         $sync $this->getEntityManager()->getRepository(OneRosterSync::class)->findOneBy(array(
  505.             'tenant' => $tenant,
  506.         ));
  507.         if ($sync instanceof OneRosterSync) {
  508.             // TODO: check that sync is not currently running
  509.             $this->getOneRosterService()->queue(
  510.                 $sync,
  511.                 OneRosterJob::PHASES[$action]
  512.             );
  513.         }
  514.         return $this->redirectToRoute(
  515.             self::ROUTES__ONE_ROSTER_LANDING,
  516.             array(
  517.                 'tenant' => $tenant->getId(),
  518.             )
  519.         );
  520.     }
  521.     /**
  522.      * @return DocumentScene
  523.      *
  524.      * @Route(
  525.      *     "/notifications-onboarding",
  526.      *     name = TenantController::ROUTES__NOTIFICATIONS_ONBOARDING,
  527.      * )
  528.      */
  529.     public function notificationsOnboardingAction(): DocumentScene
  530.     {
  531.         $tenant $this->getContextManager()->getGlobalContext()->getTenant();
  532.         $this->getContextManager()->getGlobalContext()->unlock()->setTenant(null)->lock();
  533.         $configs $this->getMessagingContext()->getAllConfigs();
  534.         // HACK: need to preload the domains as tenant stuff kicks in and is blocking certain domains from being reported on
  535.         foreach ($configs as $config) {
  536.             if ($config instanceof SendgridConfig) {
  537.                 $config->getDomains();
  538.             }
  539.         }
  540.         $this->getContextManager()->getGlobalContext()->unlock()->setTenant($tenant)->lock();
  541.         return $this->view(
  542.             array(
  543.                 'configs' => $configs,
  544.             )
  545.         );
  546.     }
  547.     /**
  548.      * @param Request $request
  549.      * @param Tenant $tenant
  550.      * @return DocumentScene|RedirectResponse
  551.      *
  552.      * @Route(
  553.      *     "/tenants/{tenant}/oneroster/delete",
  554.      *     name = TenantController::ROUTES__ONE_ROSTER_DELETE,
  555.      * )
  556.     */
  557.     public function onerosterDeleteAction(Request $requestTenant $tenant)
  558.     {
  559.         $this->getContextManager()->getGlobalContext()->unlock()->setTenant($tenant)->lock();
  560.         if ($this->getContextManager()->getGlobalContext()->getTenant() !== $tenant) {
  561.             throw $this->createAccessDeniedException();
  562.         }
  563.         $sync $this->getEntityManager()->getRepository(OneRosterSync::class)->findOneBy([
  564.             'tenant' => $tenant,
  565.         ]);
  566.         if ($sync instanceof OneRosterSync && $request->isMethod('POST')) {
  567.             $this->getOneRosterService()->delete($sync);
  568.             return $this->redirectToRoute(
  569.                 self::ROUTES__ONE_ROSTER_LANDING,
  570.                 [
  571.                     'tenant' => $tenant->getId(),
  572.                 ]
  573.             );
  574.         }
  575.         return $this->view(
  576.             [
  577.                 'sync' => $sync,
  578.             ]
  579.         );
  580.     }
  581.     /**
  582.      * @param Tenant $tenant
  583.      * @return RedirectResponse
  584.      *
  585.      * @Route(
  586.      *     "/tenants/{tenant}/oneroster/toggle-sync-status",
  587.      *     name = TenantController::ROUTES__ONE_ROSTER_TOGGLE_SYNC,
  588.      * )
  589.      *
  590.      * @ParamConverter(
  591.      *     "tenant",
  592.      *     class = Tenant::class,
  593.      * )
  594.      */
  595.     public function toggleOnerosterSyncAction(Tenant $tenant): RedirectResponse
  596.     {
  597.         $this->getContextManager()->getGlobalContext()->unlock()->setTenant($tenant)->lock();
  598.         if ($this->getContextManager()->getGlobalContext()->getTenant() !== $tenant) {
  599.             throw $this->createAccessDeniedException();
  600.         }
  601.         $sync $this->getEntityManager()->getRepository(OneRosterSync::class)->findOneBy([
  602.             'tenant' => $tenant,
  603.         ]);
  604.         if ($sync instanceof OneRosterSync) {
  605.             $this->getEntityManager()->save($sync->setActive( ! $sync->isActive()));
  606.         }
  607.         return $this->redirectToRoute(
  608.             self::ROUTES__ONE_ROSTER_LANDING,
  609.             [
  610.                 'tenant' => $tenant->getId(),
  611.             ]
  612.         );
  613.     }
  614. }