src/Cms/TenantBundle/Controller/Dashboard/SocialController.php line 75

Open in your IDE?
  1. <?php
  2. namespace Cms\TenantBundle\Controller\Dashboard;
  3. use App\Entity\System\SocialAccount;
  4. use App\Entity\System\SocialAccounts\FacebookSocialAccount;
  5. use App\Entity\System\SocialAccounts\InstagramSocialAccount;
  6. use App\Entity\System\SocialAccounts\TwitterSocialAccount;
  7. use App\Service\Social\FacebookService;
  8. use App\Service\Social\InstagramService;
  9. use App\Service\Social\TwitterService;
  10. use Cms\CoreBundle\Form\Type\ConfirmationType;
  11. use Cms\CoreBundle\Form\Type\SwitchType;
  12. use Cms\CoreBundle\Model\Scenes\DashboardScenes\DocumentScene;
  13. use Cms\CoreBundle\Util\Controller;
  14. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  15. use Symfony\Component\Routing\Annotation\Route;
  16. use Symfony\Component\Form\Extension\Core\Type\TextType;
  17. use Symfony\Component\HttpFoundation\RedirectResponse;
  18. use Symfony\Component\HttpFoundation\Request;
  19. /**
  20.  * Class SocialController
  21.  * @package Cms\TenantBundle\Controller\Dashboard
  22.  */
  23. final class SocialController extends Controller
  24. {
  25.     const ROUTES__MAIN 'cms.tenant.dashboard.social.main';
  26.     const ROUTES__CREATE 'cms.tenant.dashboard.social.create';
  27.     const ROUTES__CHOOSE_FACEBOOK 'cms.tenant.dashboard.social.choose_facebook';
  28.     const ROUTES__CHOOSE_INSTAGRAM 'cms.tenant.dashboard.social.choose_instagram';
  29.     const ROUTES__CHOOSE_TWITTER 'cms.tenant.dashboard.social.choose_twitter';
  30.     const ROUTES__TOGGLE 'cms.tenant.dashboard.social.toggle';
  31.     const ROUTES__DELETE 'cms.tenant.dashboard.social.delete';
  32.     /**
  33.      * @return InstagramService|object
  34.      */
  35.     protected function getInstagramService(): InstagramService
  36.     {
  37.         return $this->get(__METHOD__);
  38.     }
  39.     /**
  40.      * @return FacebookService|object
  41.      */
  42.     protected function getFacebookService(): FacebookService
  43.     {
  44.         return $this->get(__METHOD__);
  45.     }
  46.     /**
  47.      * @return TwitterService|object
  48.      */
  49.     protected function getTwitterService(): TwitterService
  50.     {
  51.         return $this->get(__METHOD__);
  52.     }
  53.     /**
  54.      * @return DocumentScene
  55.      *
  56.      * @Route(
  57.      *     "",
  58.      *     name = self::ROUTES__MAIN,
  59.      * )
  60.      */
  61.     public function mainAction(): DocumentScene
  62.     {
  63.         // AUDIT
  64.         $this->denyAccessUnlessGranted('app.social.admin');
  65.         return $this->view(
  66.             [
  67.                 'accounts' => $this->getEntityManager()->getRepository(SocialAccount::class)->findAll(),
  68.             ]
  69.         );
  70.     }
  71.     /**
  72.      * @param string $type
  73.      * @return RedirectResponse
  74.      *
  75.      * @Route(
  76.      *     "/accounts/create/{type}",
  77.      *     name = self::ROUTES__CREATE,
  78.      *     requirements = {
  79.      *         "type" = "facebook|twitter|instagram",
  80.      *     },
  81.      * )
  82.      */
  83.     public function createAction(string $type): RedirectResponse
  84.     {
  85.         // AUDIT
  86.         $this->denyAccessUnlessGranted('app.social.admin');
  87.         switch ($type) {
  88.             case 'facebook':
  89.                 // get url to redirect to for facebook auth
  90.                 return $this->redirect(
  91.                     $this->getFacebookService()->requestAuthenticationUrl(
  92.                         sprintf(
  93.                             'https://%s/callback/facebook',
  94.                             $this->getGlobalContext()->getDashboard(true)
  95.                         ),
  96.                         [
  97.                             'tenant' => $this->getGlobalContext()->getTenant()->getSlug(),
  98.                             'route' => self::ROUTES__CHOOSE_FACEBOOK,
  99.                         ]
  100.                     )
  101.                 );
  102.             case 'instagram':
  103.                 // get url to redirect to for facebook auth
  104.                 return $this->redirect(
  105.                     $this->getInstagramService()->requestAuthenticationUrl(
  106.                         sprintf(
  107.                             'https://%s/callback/instagram',
  108.                             $this->getGlobalContext()->getDashboard(true)
  109.                         ),
  110.                         [
  111.                             'tenant' => $this->getGlobalContext()->getTenant()->getSlug(),
  112.                             'route' => self::ROUTES__CHOOSE_INSTAGRAM,
  113.                         ]
  114.                     )
  115.                 );
  116.             case 'twitter':
  117.                 // generate the url
  118.                 $callback sprintf(
  119.                     'https://%s/callback/twitter?tenant=%s&route=%s',
  120.                     $this->getGlobalContext()->getDashboard(true),
  121.                     $this->getGlobalContext()->getTenant()->getSlug(),
  122.                     self::ROUTES__CHOOSE_TWITTER
  123.                 );
  124.                 // generate the redirect
  125.                 return $this->redirect(
  126.                     $this->getTwitterService()->setupAccount($callback)->getAbsoluteUri()
  127.                 );
  128.             default:
  129.                 throw new \Exception();
  130.         }
  131.     }
  132.     /**
  133.      * @param string|null $accessToken
  134.      * @return RedirectResponse
  135.      *
  136.      * @Route(
  137.      *     "/accounts/create/facebook/callback/{accessToken}",
  138.      *     name = self::ROUTES__CHOOSE_FACEBOOK,
  139.      *     requirements = {
  140.      *         "accessToken" = "[a-zA-Z0-9]+",
  141.      *     },
  142.      *     defaults = {
  143.      *         "accessToken" = null,
  144.      *     },
  145.      * )
  146.      */
  147.     public function facebookCallbackAction(?string $accessToken null): RedirectResponse
  148.     {
  149.         // AUDIT
  150.         $this->denyAccessUnlessGranted('app.social.admin');
  151.         // if we don't have an access token, user may have cancelled
  152.         // return to accounts
  153.         if (empty($accessToken)) {
  154.             $this->addFlash('danger',
  155.                 'System error: Facebook access token was not given.'
  156.             );
  157.             return $this->redirectToRoute(self::ROUTES__MAIN);
  158.         }
  159.         // get details about the user from facebook
  160.         $me $this->getFacebookService()->me($accessToken);
  161.         // get list of pages
  162.         $pages $this->getFacebookService()->getAccounts($accessToken);
  163.         // handle no pages
  164.         if (empty($pages)) {
  165.             $this->addFlash('warning',
  166.                 'Your Facebook account does not appear to manage any Facebook Pages.'
  167.             );
  168.             return $this->redirectToRoute(self::ROUTES__MAIN);
  169.         }
  170.         // FB page permissions are granted for all pages,
  171.         // so if not granted, we can not publish on all of them
  172.         if ( ! $this->getFacebookService()->hasRequiredScopes($accessToken)) {
  173.             $this->addFlash('warning',
  174.                 'Your Facebook account does not appear to allow our application the proper permissions to manage your Pages. Please contact support for help with this issue.'
  175.             );
  176.             return $this->redirectToRoute(self::ROUTES__MAIN);
  177.         }
  178.         // holder for db objects
  179.         $accounts = [];
  180.         // loop over each chosen page
  181.         foreach ($pages as $page) {
  182.             // create the settings objects
  183.             $account $this->getEntityManager()->getRepository(FacebookSocialAccount::class)->findOneBy([
  184.                 'facebookPageId' => $page->id,
  185.             ]);
  186.             // if not already created, make a new one
  187.             if (empty($account)) {
  188.                 $account = (new FacebookSocialAccount())
  189.                     ->setFacebookPageId($page->id);
  190.             }
  191.             // set basic details
  192.             $account
  193.                 ->setName($page->name)
  194.                 ->setFacebookAccessToken($page->token)
  195.                 ->setFacebookUserId($me->id)
  196.                 ->setFacebookUserName($me->name);
  197.             // TODO: subscribe the page to the app?
  198.             //$this->getFacebookService()->subscribe($page->token, $page->id);
  199.             // track each
  200.             $accounts[] = $account;
  201.         }
  202.         // save them all
  203.         $this->getEntityManager()->saveAll($accounts);
  204.         // record log
  205.         $this->getActivityLogger()->createLogs($accounts);
  206.         // go back to listing page
  207.         return $this->redirectToRoute(self::ROUTES__MAIN);
  208.     }
  209.     /**
  210.      * @param string|null $accessToken
  211.      * @return RedirectResponse
  212.      *
  213.      * @Route(
  214.      *     "/accounts/create/instagram/callback/{accessToken}",
  215.      *     name = self::ROUTES__CHOOSE_INSTAGRAM,
  216.      *     requirements = {
  217.      *         "accessToken" = "[a-zA-Z0-9]+",
  218.      *     },
  219.      *     defaults = {
  220.      *         "accessToken" = null,
  221.      *     },
  222.      * )
  223.      */
  224.     public function instagramCallbackAction(?string $accessToken null): RedirectResponse
  225.     {
  226.         // AUDIT
  227.         $this->denyAccessUnlessGranted('app.social.admin');
  228.         // if we don't have an access token, user may have cancelled
  229.         // return to accounts
  230.         if (empty($accessToken)) {
  231.             $this->addFlash('danger',
  232.                 'System error: Instagram access token was not given.'
  233.             );
  234.             return $this->redirectToRoute(self::ROUTES__MAIN);
  235.         }
  236.         // get details about the user from facebook
  237.         $me $this->getInstagramService()->me($accessToken);
  238.         // get list of pages
  239.         $profiles $this->getInstagramService()->getAccounts($accessToken);
  240.         // handle no pages
  241.         if (empty($profiles)) {
  242.             $this->addFlash('warning',
  243.                 'Your Meta account does not appear to manage any Instagram Profiles.'
  244.             );
  245.             return $this->redirectToRoute(self::ROUTES__MAIN);
  246.         }
  247.         // FB page permissions are granted for all pages,
  248.         // so if not granted, we can not publish on all of them
  249.         if ( ! $this->getInstagramService()->hasRequiredScopes($accessToken)) {
  250.             $this->addFlash('warning',
  251.                 'Your Meta account does not appear to allow our application the proper permissions to manage your Instagram Profiles. Please contact support for help with this issue.'
  252.             );
  253.             return $this->redirectToRoute(self::ROUTES__MAIN);
  254.         }
  255.         // holder for db objects
  256.         $accounts = [];
  257.         // loop over each chosen page
  258.         foreach ($profiles as $profile) {
  259.             // create the settings objects
  260.             $account $this->getEntityManager()->getRepository(InstagramSocialAccount::class)->findOneBy([
  261.                 'instagramProfileId' => $profile->id,
  262.             ]);
  263.             // if not already created, make a new one
  264.             if (empty($account)) {
  265.                 $account = (new InstagramSocialAccount())
  266.                     ->setInstagramProfileId($profile->id);
  267.             }
  268.             // set basic details
  269.             $account
  270.                 ->setName($profile->username)
  271.                 ->setInstagramAccessToken($profile->token)
  272.                 ->setInstagramUserId($me->id)
  273.                 ->setInstagramUserName($me->name);
  274.             // track each
  275.             $accounts[] = $account;
  276.         }
  277.         // save them all
  278.         $this->getEntityManager()->saveAll($accounts);
  279.         // record log
  280.         $this->getActivityLogger()->createLogs($accounts);
  281.         // go back to listing page
  282.         return $this->redirectToRoute(self::ROUTES__MAIN);
  283.     }
  284.     /**
  285.      * @param Request $request
  286.      * @return RedirectResponse
  287.      *
  288.      * @Route(
  289.      *     "/accounts/create/twitter/callback",
  290.      *     name = self::ROUTES__CHOOSE_TWITTER,
  291.      * )
  292.      */
  293.     public function twitterCallbackAction(Request $request): RedirectResponse
  294.     {
  295.         // AUDIT
  296.         $this->denyAccessUnlessGranted('app.social.admin');
  297.         // generate the callback url
  298.         $callback sprintf(
  299.             'https://%s/callback/twitter?tenant=%s&route=%s',
  300.             $this->getGlobalContext()->getDashboard(true),
  301.             $this->getGlobalContext()->getTenant()->getSlug(),
  302.             self::ROUTES__CHOOSE_TWITTER
  303.         );
  304.         // should have some request data
  305.         if ( ! empty($request->query->get('oauth_verifier'))) {
  306.             // obtain info about the account
  307.             $data $this->getTwitterService()->verifyCredentials(
  308.                 $request->query->get('oauth_token'),
  309.                 $request->query->get('oauth_verifier'),
  310.                 $callback
  311.             );
  312.             // try to find the new settings
  313.             $account $this->getEntityManager()->getRepository(TwitterSocialAccount::class)->findOneBy([
  314.                 'twitterUserId' => $data['id'],
  315.             ]);
  316.             // create the new settings if we don't have one
  317.             if (empty($account)) {
  318.                 $account = (new TwitterSocialAccount())
  319.                     ->setTwitterUserId($data['id']);
  320.             }
  321.             // set basic data
  322.             $account
  323.                 ->setName($data['name'])
  324.                 ->setTwitterUserName($data['screenName'])
  325.                 ->setTwitterAccessToken($data['token'])
  326.                 ->setTwitterTokenSecret($data['tokenSecret']);
  327.             // save it
  328.             $this->getEntityManager()->save($account);
  329.             // record log
  330.             $this->getActivityLogger()->createLog($account);
  331.         } else {
  332.             // notify of issue
  333.             $this->addFlash('danger',
  334.                 'System error: Twitter account could not be registered.'
  335.             );
  336.         }
  337.         return $this->redirectToRoute(self::ROUTES__MAIN);
  338.     }
  339.     /**
  340.      * @param SocialAccount $account
  341.      * @return DocumentScene|RedirectResponse
  342.      *
  343.      * @Route(
  344.      *     "/accounts/toggle/{account}",
  345.      *      requirements = {
  346.      *          "account" = "[1-9]\d*",
  347.      *     },
  348.      *     name = self::ROUTES__TOGGLE,
  349.      * )
  350.      *
  351.      * @ParamConverter(
  352.      *     "account",
  353.      *     class = "App\Entity\System\SocialAccount",
  354.      * )
  355.      */
  356.     public function toggleAction(SocialAccount $account)
  357.     {
  358.         // AUDIT
  359.         $this->denyAccessUnlessGranted('app.social.admin');
  360.         // generate the form we will be using
  361.         $form $this->createFormBuilder($account->getUsesAsToggles(), [])
  362.             ->add('name'TextType::class, [
  363.                 'mapped' => false,
  364.                 'disabled' => true,
  365.                 'data' => $account->getName(),
  366.             ]);
  367.         foreach ($account::USES as $name => $bitmask) {
  368.             $form->add($nameSwitchType::class, [
  369.                 'required' => false,
  370.             ]);
  371.         }
  372.         $form $form->getForm();
  373.         // handle submission
  374.         if ($this->handleForm($form)) {
  375.             // handle the toggling of the individual features
  376.             $toggles $form->getData();
  377.             foreach ($toggles as $name => $value) {
  378.                 $account->toggleUses($account::USES[$name], ($value == true));
  379.             }
  380.             $this->getEntityManager()->save($account);
  381.             // record log
  382.             $this->getActivityLogger()->createLog($account);
  383.             return $this->redirectToRoute(self::ROUTES__MAIN);
  384.         }
  385.         return $this->view(
  386.             [
  387.                 'account' => $account,
  388.                 'form' => $form->createView(),
  389.             ]
  390.         );
  391.     }
  392.     /**
  393.      * @param SocialAccount $account
  394.      * @return DocumentScene|RedirectResponse
  395.      *
  396.      * @Route(
  397.      *     "/accounts/delete/{account}",
  398.      *     name = self::ROUTES__DELETE,
  399.      *     requirements = {
  400.      *         "account" = "[1-9]\d*",
  401.      *     },
  402.      * )
  403.      *
  404.      * @ParamConverter(
  405.      *     "account",
  406.      *     class = "App\Entity\System\SocialAccount",
  407.      * )
  408.      */
  409.     public function deleteAction(SocialAccount $account)
  410.     {
  411.         // AUDIT
  412.         $this->denyAccessUnlessGranted('app.social.admin');
  413.         // create form, use common type
  414.         $form $this->createForm(ConfirmationType::class, [], []);
  415.         // handle submission
  416.         if ($this->handleForm($form)) {
  417.             // extract the id for use later
  418.             $id $account->getId();
  419.             // remove the account
  420.             $this->getEntityManager()->delete($account);
  421.             // record log
  422.             $this->getActivityLogger()->createLog($account$id);
  423.             return $this->redirectToRoute(self::ROUTES__MAIN);
  424.         }
  425.         return $this->view(
  426.             [
  427.                 'account' => $account,
  428.                 'form' => $form->createView(),
  429.             ]
  430.         );
  431.     }
  432. }