src/Controller/UserController.php line 273

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Production;
  4. use App\Entity\MobileAppPushToken;
  5. use App\Entity\MobileAppTicketShare;
  6. use App\Entity\User as EntityUser;
  7. use App\Form\ProfileForm;
  8. use App\Form\RegisterForm;
  9. use App\Form\SetpassForm;
  10. use App\Repository\MobileAppTicketShareRepository;
  11. use App\Repository\ProductionRepository;
  12. use App\Repository\MobileAppPushTokenRepository;
  13. use App\Security\LocalUserAuthenticator;
  14. use App\Security\TessituraGuardAuthenticator;
  15. use App\Security\MobileAppGuardAuthenticator;
  16. use App\Service\EecmService;
  17. use App\Service\TessituraBundle\AccountService;
  18. use App\Service\TessituraBundle\CartService;
  19. use App\Service\TessituraBundle\CateringServiceException;
  20. use App\Service\TessituraBundle\ConstituentService;
  21. use App\Service\TessituraBundle\CountryService;
  22. use App\Service\TessituraBundle\Item\AccountItemPerformance;
  23. use App\Service\TessituraBundle\TessituraUserProvider;
  24. use App\Service\TessituraCacheService;
  25. use App\Service\TessituraSDK\Entity\Remote\Constituent;
  26. use App\Service\TessituraSDK\Entity\Remote\Performance;
  27. use App\Service\TessituraSDK\Entity\User;
  28. use App\Service\TessituraSDK\Resource\TXN\Performances;
  29. use App\Service\TessituraSDK\TessituraClient;
  30. use App\Service\TessituraSDK\TessituraClientException;
  31. use Jsvrcek\ICS\CalendarExport;
  32. use Jsvrcek\ICS\CalendarStream;
  33. use Jsvrcek\ICS\Model\Calendar;
  34. use Jsvrcek\ICS\Model\CalendarEvent;
  35. use Jsvrcek\ICS\Model\Description\Geo;
  36. use Jsvrcek\ICS\Model\Description\Location;
  37. use Jsvrcek\ICS\Utility\Formatter;
  38. use Psr\Cache\CacheItemPoolInterface;
  39. use Psr\Log\LoggerInterface;
  40. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  41. use Symfony\Component\Routing\Annotation\Route;
  42. use Doctrine\Persistence\ManagerRegistry;
  43. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  44. use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface;
  45. use Symfony\Component\DependencyInjection\ContainerInterface;
  46. use Symfony\Component\Form\FormError;
  47. use Symfony\Component\HttpFoundation\JsonResponse;
  48. use Symfony\Component\HttpFoundation\RedirectResponse;
  49. use Symfony\Component\HttpFoundation\Request;
  50. use Symfony\Component\HttpFoundation\Response;
  51. use Symfony\Component\HttpFoundation\Session\Session;
  52. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  53. use Symfony\Component\Routing\RouterInterface;
  54. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  55. use Symfony\Component\Security\Core\Security;
  56. use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
  57. use Symfony\Component\Serializer\SerializerInterface;
  58. use Symfony\Contracts\Translation\TranslatorInterface;
  59. use App\Service\TessituraSDK\Resource\Custom;
  60. use App\Util\FirestoreHelper;
  61. use App\Util\TessituraHelper;
  62. use Doctrine\ORM\EntityManagerInterface;
  63. use GuzzleHttp\Client;
  64. use GuzzleHttp\Exception\ClientException;
  65. use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
  66. class UserController extends AbstractController
  67. {
  68.     /**
  69.      * @var TessituraClient
  70.      */
  71.     protected $client;
  72.     /**
  73.      * @var User
  74.      */
  75.     protected $user;
  76.     /**
  77.      * @var CacheItemPoolInterface|TagAwareAdapterInterface
  78.      */
  79.     protected $cache;
  80.     /**
  81.      * @var TranslatorInterface
  82.      */
  83.     private $translator;
  84.     /**
  85.      * @var \Doctrine\ORM\EntityManager
  86.      */
  87.     private $em;
  88.     /**
  89.      * @var LoggerInterface
  90.      */
  91.     private $logger;
  92.     /**
  93.      * @var ParameterBagInterface
  94.      */
  95.     private $params;
  96.     private $tessituraCacheService;
  97.     /**
  98.      * UserController constructor.
  99.      */
  100.     public function __construct(
  101.         ContainerInterface $container,
  102.         TessituraClient $client,
  103.         TranslatorInterface $translator,
  104.         ManagerRegistry $doctrine,
  105.         LoggerInterface $logger,
  106.         ParameterBagInterface $params,
  107.         TessituraCacheService $tessituraCacheService,
  108.     ) {
  109.         $this->setContainer($container);
  110.         $this->client $client;
  111.         $this->translator $translator;
  112.         $this->em $doctrine->getManager();
  113.         $this->logger $logger;
  114.         $this->params $params;
  115.         $this->cache $container->get('cache.app');
  116.         $this->tessituraCacheService $tessituraCacheService;
  117.     }
  118.     /**
  119.      * @Route(
  120.      *     "/{_locale}/register/",
  121.      *     name="register",
  122.      *     defaults={
  123.      *          "_locale": "fi"
  124.      *     },
  125.      *     requirements={
  126.      *          "_locale": "fi|sv|en"
  127.      *     }
  128.      * )
  129.      * @param Request $request
  130.      * @param RouterInterface $router
  131.      * @param TessituraUserProvider $provider
  132.      * @param GuardAuthenticatorHandler $handler
  133.      * @param CartService $cart
  134.      * @param TessituraGuardAuthenticator $authenticator
  135.      * @param ConstituentService $constituentService
  136.      *
  137.      * @return Response
  138.      * @throws TessituraClientException * @internal param ConstituentRepository $repository
  139.      */
  140.     public function registerAction(
  141.         Request $request,
  142.         RouterInterface $router,
  143.         TessituraUserProvider $provider,
  144.         GuardAuthenticatorHandler $handler,
  145.         CartService $cart,
  146.         // TessituraGuardAuthenticator $authenticator,
  147.         ConstituentService $constituentService,
  148.         Security $security,
  149.         LocalUserAuthenticator $localUserAuthenticator,
  150.         UserPasswordHasherInterface $passwordHasher,
  151.     ) {
  152.         /* @var User $user */
  153.         $user $security->getUser();
  154.         $tessituraSession TessituraHelper::getSession(
  155.             $request->getSession(),
  156.             $provider,
  157.             true
  158.         );
  159.         /**
  160.          * Get RegisterForm
  161.          */
  162.         $form $this->createForm(RegisterForm::class, [
  163.             // Prefilled values?
  164.         ]);
  165.         /**
  166.          * Handle request
  167.          */
  168.         $form->handleRequest($request);
  169.         if ($form->isSubmitted() && !$form->isValid()) {
  170.             $form->addError(new FormError($this->translator->trans("form_general_error")));
  171.         } elseif ($form->isSubmitted() && $form->isValid()) {
  172.             try {
  173.                 $data $form->getData();
  174.                 if (array_key_exists('source'$_GET)) {
  175.                     $data['source'] = $_GET['source'];
  176.                 }
  177.                 /**
  178.                  * Try to use the mobile_full created by intlTelInput.js
  179.                  * This value should contain a valid phone number with a country code
  180.                  */
  181.                 $data['mobile'] = !empty($data['mobile_full']) ? $data['mobile_full'] : $data['mobile'];
  182.                 $data['mobile_national'] = !empty($data['mobile_national'])
  183.                     ? $data['mobile_national']
  184.                     : $data['mobile'];
  185.                 $data['mobile_isocode'] = array_key_exists('mobile_isocode'$data) && !empty($data['mobile_isocode'])
  186.                     ? $data['mobile_isocode']
  187.                     : 'fi'// default: finland
  188.                 $constituent $constituentService->registerWithSessionKey($tessituraSession$data);
  189.                 $user = new \App\Entity\User();
  190.                 $user->setEmail($constituent->getEmail());
  191.                 $user->setPassword($passwordHasher->hashPassword($user$data['password']));
  192.                 $user->setConstituentId($constituent->getId());
  193.                 $user->setPhoneNumber($constituent->getPhoneNumbers()[0]->getPhoneNumber());
  194.                 $user->setPostalCode($constituent->getAddresses()[0]->getPostalCode());
  195.                 $user->setCreatedAt(new \DateTime());
  196.                 $user->setUpdatedAt(new \DateTime());
  197.                 $this->em->persist($user);
  198.                 $this->em->flush();
  199.                 /**
  200.                  * Load new user and login
  201.                  */
  202.                 // login the local user
  203.                 $handler->authenticateUserAndHandleSuccess(
  204.                     $user// Use the newly created local user
  205.                     $request,
  206.                     $localUserAuthenticator// Use LocalUserAuthenticator
  207.                     'main'
  208.                 );
  209.                 
  210. /*
  211.                 $handler->authenticateUserAndHandleSuccess(
  212.                     $provider->loadUserBySessionKey($tessituraSession),
  213.                     $request,
  214.                     $authenticator,
  215.                     'main' // The firewall
  216.                 );
  217.                 */
  218.                 if ($cart->getItemCount()) {
  219.                     $url $router->generate('checkout');
  220.                     return new RedirectResponse($url);
  221.                 } else {
  222.                     $url $router->generate('account-me');
  223.                     return new RedirectResponse($url);
  224.                 }
  225.             } catch (TessituraClientException $exception) {
  226.                 switch ($exception->getCode()) {
  227.                     case TessituraClientException::TESSITURA_DUPLICATE_ENTITY:
  228.                         $form->addError(new FormError($this->translator->trans("user_duplicate_entity")));
  229.                         break;
  230.                     default:
  231.                         throw $exception;
  232.                 }
  233.             }
  234.         }
  235.         /**
  236.          * Render
  237.          */
  238.         return $this->render('page/register.html.twig', [
  239.             'form' => $form->createView()
  240.         ]);
  241.     }
  242.     /**
  243.      * @Route(
  244.      *     "/{_locale}/api/v1/me/",
  245.      *     name="get-api-me",
  246.      *     defaults={
  247.      *          "_locale": "fi"
  248.      *     },
  249.      *     requirements={
  250.      *          "_locale": "fi|sv|en"
  251.      *     }
  252.      * )
  253.      * @param Request $request
  254.      * @param CartService $cart
  255.      * @param ConstituentService $constituentService
  256.      * @param Security $security
  257.      *
  258.      * @return JsonResponse
  259.      */
  260.     public function getMeAction(
  261.         Request $request,
  262.         CartService $cart,
  263.         ConstituentService $constituentService,
  264.         Security $security,
  265.         TessituraUserProvider $userProvider,
  266.         TessituraCacheService $tessituraCacheService,
  267.     ): JsonResponse {
  268.         $data = [];
  269.         $user $security->getUser();
  270.         if (!$user) {
  271.             return new JsonResponse([
  272.                 'success' => true,
  273.                 'authenticated' => false,
  274.                 'message' => 'Not logged in',
  275.             ]);
  276.         }
  277.         $item $tessituraCacheService->get('account_profile_' $user->getId());
  278.         try {
  279.             $redirect_to $request->get('redirect_to');
  280.             $login_link_params = !empty($redirect_to) ? ['redirect_to' => rawurlencode($redirect_to)] : [];
  281.             $profile_login_url $request->getSchemeAndHttpHost()
  282.                 . $this->generateUrl('login'$login_link_params);
  283.             $cartView $this->render('partial/cart-menu-partial.html.twig');
  284.             $profileView $this->render('partial/profile-menu-partial.html.twig', [
  285.                 'login_link' => $profile_login_url,
  286.             ]);
  287.             $tessituraSession TessituraHelper::getSession($request->getSession(), $userProvidertrue);
  288.             if (!$user) {
  289.                 $data = [
  290.                     'authenticated' => -1,
  291.                     'cart' => [
  292.                         'count' => 0,
  293.                         'diff' => 0,
  294.                         'view' => $cartView->getContent(),
  295.                         'url' => $profile_login_url,
  296.                     ],
  297.                     'profile' => [
  298.                         'view' => $profileView->getContent(),
  299.                         'url' => $profile_login_url,
  300.                         'initials' => '',
  301.                     ],
  302.                     'sessionKey' => $tessituraSession,
  303.                 ];
  304.             } else {
  305.                 $tessituraUser TessituraHelper::loginTessituraUser($user$request->getSession(), $userProvider);
  306.                 $constituent $constituentService->getConstituent($tessituraUser->getConstituentId());
  307.                 $data = [
  308.                     'authenticated' => 1,
  309.                     'cart' => [
  310.                         'count' => $cart->getItemCount(),
  311.                         'diff' => $tessituraUser->getExpirationDiff()->60 $tessituraUser->getExpirationDiff()->s,
  312.                         'view' => $cartView->getContent(),
  313.                         'url' => $request->getSchemeAndHttpHost() . $this->generateUrl('cart'),
  314.                     ],
  315.                     'profile' => [
  316.                         'view' => $profileView->getContent(),
  317.                         'url' => $request->getSchemeAndHttpHost() . $this->generateUrl('account-me'),
  318.                         'initials' => mb_substr($constituent->getFirstName(), 01)
  319.                             . mb_substr($constituent->getLastName(), 01),
  320.                     ],
  321.                     'sessionKey' => $tessituraSession,
  322.                 ];
  323.             }
  324.             return new JsonResponse(array_merge([
  325.                 'success' => true,
  326.             ], $data));
  327.         } catch (\Exception $exception) {
  328.             error_log("getMeAction error: " print_r($exception->getMessage(), true));
  329.         }
  330.         return new JsonResponse(array_merge([
  331.             'success' => false
  332.         ], $data));
  333.     }
  334.     private function getPushTokens(array $constituentIds)
  335.     {
  336.         /** @var MobileAppPushTokenRepository $repository */
  337.         $repository $this->em->getRepository(MobileAppPushToken::class);
  338.         return $repository->findByConstituents($constituentIds);
  339.     }
  340.     /**
  341.      * @Route(
  342.      *     "/api/v1/authenticated/",
  343.      *     name="post-authenticated",
  344.      *       methods={"POST"}
  345.      * )
  346.      * @param Request $request
  347.      * @param TessituraUserProvider $provider
  348.      *
  349.      * @return Response
  350.      */
  351.     public function userIsAuthenticated(Request $requestTessituraUserProvider $providerSecurity $security)
  352.     {
  353.         $data = [];
  354.         $user $security->getUser();
  355.         
  356.         if ($user) {
  357.             // this is a local user
  358.             $this->logger->debug('API isAuthenticated query success for local user', [
  359.                 'user' => $user,
  360.             ]);
  361.             return new JsonResponse(array_merge([
  362.                 'success' => true,
  363.                 'authenticated' => true
  364.             ], $data));
  365.         }
  366.         $requestData $request->toArray();
  367.         $sessionKey $requestData['sessionKey'];
  368.         if (!$sessionKey) {
  369.             return new JsonResponse(array_merge([
  370.                 'success' => false,
  371.                 'authenticated' => false
  372.             ], $data));
  373.         }
  374.         
  375.         try {
  376.             $user $provider->loadUserBySessionKey($sessionKey);
  377.             if ($user->isPseudoFlag()) {
  378.                 $data = [
  379.                     'authenticated' => false,
  380.                 ];
  381.             } else {
  382.                 $data = [
  383.                     'authenticated' => in_array('ROLE_TESSITURA_KNOWN'$user->getRoles()),
  384.                 ];
  385.             }
  386.             return new JsonResponse(array_merge([
  387.                 'success' => true
  388.             ], $data));
  389.         } catch (\Exception $exception) {
  390.         }
  391.         return new JsonResponse(array_merge([
  392.             'success' => false,
  393.             'authenticated' => false,
  394.         ], $data));
  395.     }
  396.     /**
  397.      * @Route(
  398.      *     "/{_locale}/api/v1/user-data/add-favorite/",
  399.      *     name="post-add-favorite",
  400.      *       methods={"POST"},
  401.      *     defaults={
  402.      *          "_locale": "fi"
  403.      *     },
  404.      *     requirements={
  405.      *          "_locale": "fi|sv|en"
  406.      *     }
  407.      * )
  408.      * @param Request $request
  409.      *
  410.      * @return Response
  411.      */
  412.     public function addUserFavorite(
  413.         Request $request,
  414.         Security $security,
  415.     ) {
  416.         $data = [];
  417.         $requestData $request->toArray();
  418.         $favorite $requestData['favorite'] ?? false;
  419.         if (!$favorite) {
  420.             return new JsonResponse(array_merge([
  421.                 'success' => false,
  422.                 'message' => 'No favorite'
  423.             ], $data), 500);
  424.         }
  425.         /** @var TokenInterface $token */
  426.         $token $security->getToken();
  427.         if (!$token) {
  428.             return new JsonResponse(array_merge([
  429.                 'success' => false,
  430.                 'message' => 'No token'
  431.             ], $data), 500);
  432.         }
  433.         /** @var User $user */
  434.         $user $token->getUser();
  435.         try {
  436.             $success false;
  437.             if ($user->isPseudoFlag()) {
  438.                 $data = [
  439.                     'authenticated' => false,
  440.                     'message' => 'Not logged in'
  441.                 ];
  442.                 return new JsonResponse(array_merge([
  443.                     'success' => $success
  444.                 ], $data), 403);
  445.             } else {
  446.                 $authenticated in_array('ROLE_TESSITURA_KNOWN'$user->getRoles());
  447.                 $status $authenticated 200 403;
  448.                 if ($authenticated) {
  449.                     $client = new Client();
  450.                     $environment $this->params->get('wp.cookie_env');
  451.                     $payload = [
  452.                         'json' => [
  453.                             'user' => md5($user->getConstituentId()),
  454.                             'environment' => $environment,
  455.                             'favorite' => (int)$favorite
  456.                         ]
  457.                     ];
  458.                     try {
  459.                         $response $client->post(
  460.                             $this->params->get('firestore.api.add_favorite'),
  461.                             $payload
  462.                         );
  463.                         $responseStatus $response->getStatusCode();
  464.                         $responseBody \GuzzleHttp\json_decode($response->getBody()->getContents(), true);
  465.                         $success true;
  466.                     } catch (\Exception $exception) {
  467.                         error_log(print_r($exception->getMessage(), true));
  468.                         $responseStatus 500;
  469.                         $status 500;
  470.                         $responseBody = [];
  471.                         $success false;
  472.                         $data = [
  473.                             'message' => 'Failed to add favorite'
  474.                         ];
  475.                     }
  476.                     if ($responseStatus === 200 && $responseBody['success']) {
  477.                         $data = [
  478.                             'user' => $responseBody['user'],
  479.                             'message' => $responseBody['message']
  480.                         ];
  481.                     }
  482.                     $data['authenticated'] = $authenticated;
  483.                     $cid $user->getConstituentId();
  484.                     $cacheKeys = [
  485.                         'tvapp-usercontent-fi-' $cid,
  486.                         'tvapp-usercontent-sv-' $cid,
  487.                         'tvapp-usercontent-en-' $cid,
  488.                     ];
  489.                     $this->cache->invalidateTags($cacheKeys);
  490.                     return new JsonResponse(array_merge([
  491.                         'success' => $success
  492.                     ], $data), $status);
  493.                 }
  494.             }
  495.         } catch (\Exception $exception) {
  496.         }
  497.         return new JsonResponse(array_merge([
  498.             'success' => false,
  499.             'authenticated' => false,
  500.             'message' => 'Not logged in (error)',
  501.         ], $data), 403);
  502.     }
  503.     /**
  504.      * @Route(
  505.      *     "/{_locale}/api/v1/user-data/remove-favorite/",
  506.      *     name="post-remove-favorite",
  507.      *       methods={"POST"},
  508.      *     defaults={
  509.      *          "_locale": "fi"
  510.      *     },
  511.      *     requirements={
  512.      *          "_locale": "fi|sv|en"
  513.      *     }
  514.      * )
  515.      * @param Request $request
  516.      *
  517.      * @return Response
  518.      */
  519.     public function removeUserFavorite(
  520.         Request $request,
  521.         Security $security,
  522.     ): JsonResponse {
  523.         $data = [];
  524.         $requestData $request->toArray();
  525.         $favorite $requestData['favorite'] ?? false;
  526.         if (!$favorite) {
  527.             return new JsonResponse(array_merge([
  528.                 'success' => false,
  529.                 'message' => 'No favorite'
  530.             ], $data), 500);
  531.         }
  532.         $token $security->getToken();
  533.         if (!$token) {
  534.             return new JsonResponse(array_merge([
  535.                 'success' => false,
  536.                 'message' => 'No token'
  537.             ], $data), 500);
  538.         }
  539.         /** @var User $user */
  540.         $user $token->getUser();
  541.         try {
  542.             $success false;
  543.             if ($user->isPseudoFlag()) {
  544.                 $data = [
  545.                     'authenticated' => false,
  546.                     'message' => 'Not logged in'
  547.                 ];
  548.                 return new JsonResponse(array_merge([
  549.                     'success' => $success
  550.                 ], $data), 403);
  551.             } else {
  552.                 $authenticated in_array('ROLE_TESSITURA_KNOWN'$user->getRoles());
  553.                 $status $authenticated 200 403;
  554.                 if ($authenticated) {
  555.                     $client = new Client();
  556.                     $environment $this->params->get('wp.cookie_env');
  557.                     $payload = [
  558.                         'json' => [
  559.                             'user' => md5($user->getConstituentId()),
  560.                             'environment' => $environment,
  561.                             'favorite' => (int)$favorite
  562.                         ]
  563.                     ];
  564.                     try {
  565.                         $response $client->delete(
  566.                             $this->params->get('firestore.api.remove_favorite'),
  567.                             $payload
  568.                         );
  569.                         $responseStatus $response->getStatusCode();
  570.                         $responseBody \GuzzleHttp\json_decode($response->getBody()->getContents(), true);
  571.                         $success true;
  572.                     } catch (\Exception $exception) {
  573.                         error_log(print_r($exception->getMessage(), true));
  574.                         $responseStatus 500;
  575.                         $status 500;
  576.                         $responseBody = [];
  577.                         $success false;
  578.                         $data = [
  579.                             'message' => 'Failed to remove favorite'
  580.                         ];
  581.                     }
  582.                     if ($responseStatus === 200 && $responseBody['success']) {
  583.                         $data = [
  584.                             'user' => $responseBody['user'],
  585.                             'message' => $responseBody['message']
  586.                         ];
  587.                     }
  588.                     $data['authenticated'] = $authenticated;
  589.                     $cid $user->getConstituentId();
  590.                     $cacheKeys = [
  591.                         'tvapp-usercontent-fi-' $cid,
  592.                         'tvapp-usercontent-sv-' $cid,
  593.                         'tvapp-usercontent-en-' $cid,
  594.                     ];
  595.                     $this->cache->invalidateTags($cacheKeys);
  596.                     return new JsonResponse(array_merge([
  597.                         'success' => $success
  598.                     ], $data), $status);
  599.                 }
  600.             }
  601.         } catch (\Exception $exception) {
  602.         }
  603.         return new JsonResponse(array_merge([
  604.             'success' => false,
  605.             'authenticated' => false,
  606.             'message' => 'Not logged in (error)',
  607.         ], $data), 403);
  608.     }
  609.     /**
  610.      * @Route(
  611.      *     "/{_locale}/api/v1/user-data/update-watch-time/",
  612.      *     name="post-update-watch-time",
  613.      *       methods={"POST"},
  614.      *     defaults={
  615.      *          "_locale": "fi"
  616.      *     },
  617.      *     requirements={
  618.      *          "_locale": "fi|sv|en"
  619.      *     }
  620.      * )
  621.      * @param Request $request
  622.      *
  623.      * @return Response
  624.      */
  625.     public function updateWatchTime(
  626.         Request $request,
  627.         Security $security,
  628.     ) {
  629.         $data = [];
  630.         $requestData $request->toArray();
  631.         $video $requestData['video'] ?? false;
  632.         $progress $requestData['progress'] ?? false;
  633.         $progressPercent $requestData['progressPercent'] ?? false;
  634.         if (!$progress || !$progressPercent || !$video) {
  635.             return new JsonResponse(array_merge([
  636.                 'success' => false,
  637.                 'message' => 'Bad request'
  638.             ], $data), 400);
  639.         }
  640.         $token $security->getToken();
  641.         if (!$token) {
  642.             return new JsonResponse(array_merge([
  643.                 'success' => false,
  644.                 'message' => 'No token'
  645.             ], $data), 500);
  646.         }
  647.         /** @var User $user */
  648.         $user $token->getUser();
  649.         try {
  650.             $success false;
  651.             if ($user->isPseudoFlag()) {
  652.                 $data = [
  653.                     'authenticated' => false,
  654.                     'message' => 'Not logged in'
  655.                 ];
  656.                 return new JsonResponse(array_merge([
  657.                     'success' => $success
  658.                 ], $data), 403);
  659.             } else {
  660.                 $authenticated in_array('ROLE_TESSITURA_KNOWN'$user->getRoles());
  661.                 $status $authenticated 200 403;
  662.                 if ($authenticated) {
  663.                     $client = new Client();
  664.                     $environment $this->params->get('wp.cookie_env');
  665.                     $payload = [
  666.                         'json' => [
  667.                             'user' => md5($user->getConstituentId()),
  668.                             'environment' => $environment,
  669.                             'video' => (int)$video,
  670.                             'progress' => (int)$progress,
  671.                             'progressPercent' => (int)$progressPercent
  672.                         ]
  673.                     ];
  674.                     try {
  675.                         $response $client->post(
  676.                             $this->params->get('firestore.api.update_watch_time'),
  677.                             $payload
  678.                         );
  679.                         $responseStatus $response->getStatusCode();
  680.                         $responseBody \GuzzleHttp\json_decode($response->getBody()->getContents(), true);
  681.                         $success true;
  682.                     } catch (\Exception $exception) {
  683.                         error_log(print_r($exception->getMessage(), true));
  684.                         $responseStatus 500;
  685.                         $status 500;
  686.                         $responseBody = [];
  687.                         $success false;
  688.                         $data = [
  689.                             'message' => 'Failed to update watch time'
  690.                         ];
  691.                     }
  692.                     if ($responseStatus === 200 && $responseBody['success']) {
  693.                         $data = [
  694.                             'user' => $responseBody['user'],
  695.                             'message' => $responseBody['message']
  696.                         ];
  697.                     }
  698.                     $data['authenticated'] = $authenticated;
  699.                     return new JsonResponse(array_merge([
  700.                         'success' => $success
  701.                     ], $data), $status);
  702.                 }
  703.             }
  704.         } catch (\Exception $exception) {
  705.         }
  706.         return new JsonResponse(array_merge([
  707.             'success' => false,
  708.             'authenticated' => false,
  709.             'message' => 'Not logged in (error)',
  710.         ], $data), 403);
  711.     }
  712.     /**
  713.      * @Route(
  714.      *     "/{_locale}/api/v1/user-data/",
  715.      *     name="get-user-data",
  716.      *       methods={"GET"},
  717.      *     defaults={
  718.      *          "_locale": "fi"
  719.      *     },
  720.      *     requirements={
  721.      *          "_locale": "fi|sv|en"
  722.      *     }
  723.      * )
  724.      * @param Request $request
  725.      * @param Security $security
  726.      * @param AccountService $accountService
  727.      * @param TessituraUserProvider $provider
  728.      * @param MobileAppGuardAuthenticator $guardAuthenticator
  729.      *
  730.      * @return JsonResponse
  731.      */
  732.     public function getUserData(
  733.         Request $request,
  734.         Security $security,
  735.         AccountService $accountService,
  736.         TessituraUserProvider $provider,
  737.         MobileAppGuardAuthenticator $guardAuthenticator
  738.     ): JsonResponse {
  739.         $data = [];
  740.         $session $request->getSession();
  741.         if ($request->headers->get('Authorization')) {
  742.             // authorization: bearer token
  743.             try {
  744.                 $user $guardAuthenticator->getUser($request->headers->get('Authorization'), $provider);
  745.             } catch (\Exception $exception) {
  746.                 $statusCode $exception->getMessage() === 'Token expired' 401 500;
  747.                 return new JsonResponse(array_merge([
  748.                     'success' => false,
  749.                     'message' => $exception->getMessage(),
  750.                 ], $data), $statusCode);
  751.             }
  752.         } else {
  753.             $user $security->getUser();
  754.         }
  755.         if (!$user) {
  756.             return new JsonResponse(array_merge([
  757.                 'success' => false,
  758.                 'message' => 'No token'
  759.             ], $data), 500);
  760.         }
  761.         $fh = new FirestoreHelper($this->params$user->getConstituentId());
  762.         $firestoredata $fh->getUserData();
  763.         if (!$firestoredata) {
  764.             return new JsonResponse(array_merge([
  765.                 'success' => false,
  766.                 'message' => 'Failed to get user data'
  767.             ], $data), 500);
  768.         }
  769.         $data = [
  770.             'user' => $firestoredata,
  771.             'authenticated' => true,
  772.         ];
  773.         $streams $this->tessituraCacheService->get('account_streams_' $user->getId());
  774.         if (!$streams) {
  775.             $tessituraUser TessituraHelper::loginTessituraUser($user$session$provider);
  776.             $streams array_merge([], $accountService->getPurchasedFeeProducts($tessituraUser->getSessionKey()));
  777.             $this->tessituraCacheService->put('account_streams_' $user->getId(), $streams);
  778.         }
  779.         
  780.         // user is authenticated, so return 200 even if firebase user can't be found
  781.         return new JsonResponse(array_merge([
  782.             'success' => true,
  783.             'streams' => $streams,
  784.         ], $data), 200);
  785.         return new JsonResponse(array_merge([
  786.             'success' => true,
  787.             'authenticated' => false,
  788.             'message' => 'Not logged (error)',
  789.         ], $data), 403);
  790.     }
  791.     /**
  792.      * @Route(
  793.      *     "/{_locale}/account/profile/",
  794.      *     name="account-profile",
  795.      *     defaults={
  796.      *          "_locale": "fi"
  797.      *     },
  798.      *     requirements={
  799.      *          "_locale": "fi|sv|en"
  800.      *     }
  801.      * )
  802.      * @param Request $request
  803.      * @param ConstituentService $constituentService
  804.      *
  805.      * @return Response
  806.      * @throws TessituraClientException
  807.      * @internal param ConstituentRepository $repository
  808.      */
  809.     public function profileAction(
  810.         Request $request,
  811.         ConstituentService $constituentService,
  812.         Security $security,
  813.         CountryService $countryService,
  814.         TessituraCacheService $tessituraCacheService,
  815.         EntityManagerInterface $entityManager,
  816.     ) {
  817.         /** @var \App\Entity\User $user */
  818.         $user $security->getUser();
  819.         $item $tessituraCacheService->get('account_profile_' $user->getId());
  820.         if ($item) {
  821.             $formData json_decode($itemtrue);
  822.         } else {
  823.             $constituent $constituentService->getConstituent();
  824.             $defaultPhoneCountryCode $countryService->idToCode(
  825.                 $constituent->getDefaultPhoneNumber()->getCountry()->getId()
  826.             );
  827.             $formData = [
  828.                 'firstname' => $constituent->getFirstName(),
  829.                 'lastname' => $constituent->getLastName(),
  830.                 'email' => $constituent->getEmail(),
  831.                 'street1' => $constituent->getStreet1(),
  832.                 'postalcode' => $constituent->getPostalCode(),
  833.                 'city' => $constituent->getCity(),
  834.                 'mobile' => $constituent->getMobile(),
  835.                 'mobile_full' => $constituent->getMobileFull(),
  836.                 'mobile_isocode' => $defaultPhoneCountryCode,
  837.                 'opt_in' => $constituent->getOptIn(),
  838.                 'opt_in_kv' => $constituent->getOptInInterest(),
  839.             ];
  840.             /**
  841.              * Check if user has an temporary address and clear these
  842.              */
  843.             if ($constituent->getStreet1() === $this->params->get('tessitura.subscribe.default_street1')) {
  844.                 unset($formData['street1']);
  845.                 unset($formData['postalcode']);
  846.                 unset($formData['city']);
  847.                 unset($formData['mobile']);
  848.                 unset($formData['mobile_full']);
  849.             }
  850.             $tessituraCacheService->put('account_profile_' $user->getId(), json_encode($formData));
  851.         }
  852.         $form $this->createForm(ProfileForm::class, $formData);
  853.         $form->handleRequest($request);
  854.         if ($form->isSubmitted() && $form->isValid()) {
  855.             $tessituraCacheService->delete('account_profile_' $user->getId());
  856.             try {
  857.                 /** @var array $data */
  858.                 $data $form->getData();
  859.                 $data['mobile'] = !empty($data['mobile_full'])
  860.                     ? $data['mobile_full']
  861.                     : $data['mobile'];
  862.                 $data['mobile_national'] = !empty($data['mobile_national'])
  863.                     ? $data['mobile_national']
  864.                     : $data['mobile'];
  865.                 $constituentService->update($data);
  866.                 $user->setEmail($data['email']);
  867.                 $user->setPhoneNumber($data['mobile']);
  868.                 $user->setPostalCode($data['postalcode']);
  869.                 $entityManager->persist($user);
  870.                 $entityManager->flush();
  871.                 $this->logger->info('User updated', [
  872.                     'user' => $user,
  873.                     'data' => $data
  874.                 ]);
  875.                 $form->addError(new FormError($this->translator->trans("user_updated")));
  876.             } catch (TessituraClientException $exception) {
  877.                 switch ($exception->getCode()) {
  878.                     case TessituraClientException::TESSITURA_INVALID_EMAIL_ADDRESS:
  879.                         $form->addError(new FormError($this->translator->trans("user_invalid_email")));
  880.                         break;
  881.                     case TessituraClientException::TESSITURA_INVALID_CREDENTIALS:
  882.                         $form->addError(new FormError($this->translator->trans("user_invalid_password")));
  883.                         break;
  884.                     case TessituraClientException::TESSITURA_DUPLICATE_ENTITY:
  885.                         $form->addError(new FormError($this->translator->trans("user_duplicate_entity")));
  886.                         break;
  887.                     default:
  888.                         throw $exception;
  889.                 }
  890.             }
  891.         }
  892.         return $this->render('page/account-profile.html.twig', [
  893.             'form' => $form->createView(),
  894.         ]);
  895.     }
  896.     /**
  897.      * @Route(
  898.      *     "/{_locale}/account/staff-tickets/",
  899.      *     name="staff-tickets",
  900.      *     defaults={
  901.      *          "_locale": "fi"
  902.      *     },
  903.      *     requirements={
  904.      *          "_locale": "fi|sv|en"
  905.      *     }
  906.      * )
  907.      * @param ConstituentService $constituentService
  908.      *
  909.      * @return Response
  910.      * @throws TessituraClientException
  911.      * @internal param ConstituentRepository $repository
  912.      */
  913.     public function staffTicketsAction(ConstituentService $constituentService)
  914.     {
  915.         // Not in use since season 24/25 but these might come back at some point
  916.         // causes errors in DEV as the procedure isn't available so commented out for now
  917.         //
  918.         // $constituent = $constituentService->getConstituent();
  919.         // $customRequest = Custom::getAll("StaffTicketCodes?customer_no=" . $constituent->getId());
  920.         // try {
  921.         //  $response = $this->client->request($customRequest);
  922.         //  $staffTickets = [];
  923.         //  $now = new \DateTime('now');
  924.         //  if (!empty((array)$response->toArray())) {
  925.         //      foreach ((array)$response->toArray() as $row) {
  926.         //          $endTime = new \DateTime();
  927.         //          $endTime->modify($row['end_dt']);
  928.         //          if ($now->getTimestamp() <= $endTime->getTimestamp()) {
  929.         //              $staffTickets[] = [
  930.         //                  'code' => $row['code'],
  931.         //                  'active' => is_null($row['sli_no']),
  932.         //                  'expires' => $endTime->format('d.m.Y')
  933.         //              ];
  934.         //          }
  935.         //      }
  936.         //      // Show active ones first
  937.         //      usort($staffTickets, function($a, $b) {
  938.         //          return $b['active'] <=> $a['active'];
  939.         //      });
  940.         //  }
  941.         // } catch (TessituraClientException $exception) {
  942.         //  $staffTickets = [];
  943.         // }
  944.         return $this->render('page/account-staff-tickets.html.twig', [
  945.             'tickets' => [],
  946.         ]);
  947.     }
  948.     /**
  949.      * @Route(
  950.      *     "/{_locale}/account/subscribers/",
  951.      *     name="subscribers",
  952.      *     defaults={
  953.      *          "_locale": "fi"
  954.      *     },
  955.      *     requirements={
  956.      *          "_locale": "fi|sv|en"
  957.      *     }
  958.      * )
  959.      * @param ConstituentService $constituentService
  960.      *
  961.      * @return Response
  962.      * @throws TessituraClientException
  963.      * @internal param ConstituentRepository $repository
  964.      */
  965.     public function subscribersAction(ConstituentService $constituentService)
  966.     {
  967.         return $this->render('page/account-subscribers.html.twig', [
  968.         ]);
  969.     }
  970.     /**
  971.      * @Route(
  972.      *     "/{_locale}/login/profile/",
  973.      *     name="login-profile",
  974.      *     defaults={
  975.      *          "_locale": "fi"
  976.      *     },
  977.      *     requirements={
  978.      *          "_locale": "fi|sv|en"
  979.      *     }
  980.      * )
  981.      * @param Request $request
  982.      * @param ConstituentService $constituentService
  983.      * @param CartService $cartService
  984.      *
  985.      * @return Response
  986.      * @throws TessituraClientException * @internal param ConstituentRepository $repository
  987.      */
  988.     public function loginProfileAction(
  989.         Request $request,
  990.         ConstituentService $constituentService,
  991.         CartService $cartService,
  992.         Security $security,
  993.     ): Response {
  994.         /* why? */
  995.         /* $user = $security->getUser(); */
  996.         $constituent $constituentService->getConstituent();
  997.         $formData = [
  998.             'firstname' => $constituent->getFirstName(),
  999.             'lastname' => $constituent->getLastName(),
  1000.             'email' => $constituent->getEmail(),
  1001.             'street1' => $constituent->getStreet1(),
  1002.             'postalcode' => $constituent->getPostalCode(),
  1003.             'city' => $constituent->getCity(),
  1004.             'mobile' => $constituent->getMobile(),
  1005.             'mobile_full' => $constituent->getMobileFull(),
  1006.             'opt_in' => $constituent->getOptIn(),
  1007.             'opt_in_kv' => $constituent->getOptInInterest(),
  1008.         ];
  1009.         /**
  1010.          * Check if user has an temporary address and clear these
  1011.          */
  1012.         if ($constituent->getStreet1() === $this->params->get('tessitura.subscribe.default_street1')) {
  1013.             unset($formData['street1']);
  1014.             unset($formData['postalcode']);
  1015.             unset($formData['postalcode']);
  1016.             unset($formData['city']);
  1017.             unset($formData['mobile']);
  1018.             unset($formData['mobile_full']);
  1019.         }
  1020.         $form $this->createForm(ProfileForm::class, $formData);
  1021.         $form->handleRequest($request);
  1022.         if ($form->isSubmitted() && $form->isValid()) {
  1023.             try {
  1024.                 /** @var array $data */
  1025.                 $data $form->getData();
  1026.                 $data['mobile'] = !empty($data['mobile_full']) ? $data['mobile_full'] : $data['mobile'];
  1027.                 $data['mobile_national'] = !empty($data['mobile_national'])
  1028.                     ? $data['mobile_national']
  1029.                     : $data['mobile'];
  1030.                 $constituentService->update($data);
  1031.                 if ($cartService->getItemCount()) {
  1032.                     return $this->redirectToRoute('checkout');
  1033.                 } else {
  1034.                     return $this->redirectToRoute('account-me');
  1035.                 }
  1036.             } catch (TessituraClientException $exception) {
  1037.                 switch ($exception->getCode()) {
  1038.                     case TessituraClientException::TESSITURA_INVALID_EMAIL_ADDRESS:
  1039.                         $form->addError(new FormError($this->translator->trans("user_invalid_email")));
  1040.                         break;
  1041.                     case TessituraClientException::TESSITURA_INVALID_CREDENTIALS:
  1042.                         $form->addError(new FormError($this->translator->trans("user_invalid_password")));
  1043.                         break;
  1044.                     case TessituraClientException::TESSITURA_DUPLICATE_ENTITY:
  1045.                         $form->addError(new FormError($this->translator->trans("user_duplicate_entity")));
  1046.                         break;
  1047.                     default:
  1048.                         throw $exception;
  1049.                 }
  1050.             }
  1051.         }
  1052.         return $this->render('page/login-profile.html.twig', [
  1053.             'form' => $form->createView(),
  1054.         ]);
  1055.     }
  1056.     /**
  1057.      * @Route(
  1058.      *     "/{_locale}/account/password/",
  1059.      *     name="account-password",
  1060.      *     defaults={
  1061.      *          "_locale": "fi"
  1062.      *     },
  1063.      *     requirements={
  1064.      *          "_locale": "fi|sv|en"
  1065.      *     }
  1066.      * )
  1067.      * @param Request $request
  1068.      * @param ConstituentService $constituentService
  1069.      * @param CartService $cartService
  1070.      *
  1071.      * @return Response
  1072.      * @throws TessituraClientException
  1073.      */
  1074.     public function setPasswordAction(
  1075.         Request $request,
  1076.         ConstituentService $constituentService,
  1077.         CartService $cartService,
  1078.         Security $security,
  1079.         TessituraUserProvider $tessituraUserProvider,
  1080.         UserPasswordHasherInterface $passwordHasher,
  1081.         EntityManagerInterface $entityManager,
  1082.     ): Response {
  1083.         /* @var User $user */
  1084.         $user $security->getUser();
  1085.         $tessituraUser TessituraHelper::loginTessituraUser(
  1086.             $user,
  1087.             $request->getSession(),
  1088.             $tessituraUserProvider
  1089.         );
  1090.         $constituent = clone $constituentService->getConstituent();
  1091.         $defaultWebLogin $constituent->getDefaultWebLogin();
  1092.         $email $defaultWebLogin->getLogin();
  1093.         $form $this->createForm(SetpassForm::class, $constituent, [
  1094.             'ask_current_password' => !$constituent->getIsTemporary()
  1095.         ]);
  1096.         $form->handleRequest($request);
  1097.         if ($form->isSubmitted() && $form->isValid()) {
  1098.             try {
  1099.                 /** @var Constituent $data */
  1100.                 $data $form->getData();
  1101.                 $constituentService->savePassword(
  1102.                     $constituent->getId(),
  1103.                     $tessituraUser,
  1104.                     $data,
  1105.                     $constituentService->getConstituent(),
  1106.                     $email
  1107.                 );
  1108.                 $user->setPassword($passwordHasher->hashPassword($user$data->getPassword()));
  1109.                 $entityManager->persist($user);
  1110.                 $entityManager->flush();
  1111.                 $form->addError(new FormError($this->translator->trans("user_updated")));
  1112.                 if ($cartService->getItemCount()) {
  1113.                     return $this->redirectToRoute('checkout');
  1114.                 }
  1115.                 if ($constituentService->isInvalid()) {
  1116.                     return $this->redirectToRoute('login-profile');
  1117.                 }
  1118.                 $url $this->generateUrl('account-me', ['passwd' => 'ok']);
  1119.                 return new RedirectResponse($url);
  1120.             } catch (TessituraClientException $exception) {
  1121.                 switch ($exception->getCode()) {
  1122.                     case TessituraClientException::TESSITURA_INVALID_CREDENTIALS:
  1123.                         $form->addError(new FormError($this->translator->trans("user_invalid_password")));
  1124.                         break;
  1125.                     default:
  1126.                         throw $exception;
  1127.                 }
  1128.             }
  1129.         }
  1130.         return $this->render('page/account-password.html.twig', [
  1131.             'form' => $form->createView(),
  1132.         ]);
  1133.     }
  1134.     /**
  1135.      * @Route(
  1136.      *     "/{_locale}/account/order/{orderId}/",
  1137.      *     name="order",
  1138.      *     defaults={
  1139.      *          "_locale": "fi"
  1140.      *     },
  1141.      *     requirements={
  1142.      *          "_locale": "fi|sv|en"
  1143.      *     }
  1144.      * )
  1145.      * @param Request $request
  1146.      * @param AccountService $accountService
  1147.      * @param $orderId
  1148.      *
  1149.      * @return \Symfony\Component\HttpFoundation\Response
  1150.      */
  1151.     public function showOrderAction(
  1152.         // Request $request,
  1153.         AccountService $accountService,
  1154.         EecmService $eecmService,
  1155.         Security $security,
  1156.         $orderId,
  1157.     ) {
  1158.         $user $security->getUser();
  1159.         /* Why? */
  1160.         /* $session = $request->getSession(); */
  1161.         try {
  1162.             $order $accountService->getOrder((int)$orderId);
  1163.             // Bail if this is not users orders
  1164.             if ($order->getConstituent()->getId() !== $user->getConstituentId()) {
  1165.                 throw $this->createNotFoundException();
  1166.             }
  1167.         } catch (TessituraClientException $exception) {
  1168.             throw $this->createNotFoundException();
  1169.         }
  1170.         $products $accountService->getProducts($order);
  1171.         try {
  1172.             $productsFormatted $eecmService->formatProducts($products);
  1173.             $eecmDataLayer = [
  1174.                 'transaction_id' => $order->getId(),
  1175.                 'value' => $accountService->getTotal($order),
  1176.                 'tax' => array_reduce(
  1177.                     $accountService->getVatAmounts($order), // TODO: returns null?
  1178.                     function ($carry$item) {
  1179.                         $carry += $item;
  1180.                         return $carry;
  1181.                     }
  1182.                 ),
  1183.                 'shipping' => 0,
  1184.                 'currency' => 'EUR',
  1185.                 //'coupon' => '', // TODO: how to get?
  1186.                 'items' => $productsFormatted,
  1187.             ];
  1188.         } catch (\Exception $exception) {
  1189.             $eecmDataLayer = [];
  1190.             error_log($exception->getMessage());
  1191.             $this->logger->warning('Error generating purchase data:' $exception->getMessage());
  1192.         }
  1193.         return $this->render('page/complete.html.twig', [
  1194.             'order' => $order,
  1195.             'datalayer' => [
  1196.                 'event' => 'purchase',
  1197.                 'ecommerce' => $eecmDataLayer,
  1198.             ],
  1199.         ]);
  1200.     }
  1201.     /**
  1202.      * @Route(
  1203.      *     "/{_locale}/account/gifts/",
  1204.      *     name="account-gifts",
  1205.      *     defaults={
  1206.      *          "_locale": "fi"
  1207.      *     },
  1208.      *     requirements={
  1209.      *          "_locale": "fi|sv|en"
  1210.      *     }
  1211.      * )
  1212.      * @param Request $request
  1213.      *
  1214.      * @return Response
  1215.      * @internal param ConstituentRepository $repository
  1216.      */
  1217.     public function giftsAction(Request $request)
  1218.     {
  1219.         return $this->render('page/account-purchases.html.twig', [
  1220.         ]);
  1221.     }
  1222.     /**
  1223.      * @Route(
  1224.      *     "/{_locale}/account/faq/",
  1225.      *     name="account-faq",
  1226.      *     defaults={
  1227.      *          "_locale": "fi"
  1228.      *     },
  1229.      *     requirements={
  1230.      *          "_locale": "fi|sv|en"
  1231.      *     }
  1232.      * )
  1233.      * @param Request $request
  1234.      *
  1235.      * @return Response
  1236.      * @internal param ConstituentRepository $repository
  1237.      */
  1238.     public function faqAction(Request $request)
  1239.     {
  1240.         return $this->render('page/account-purchases.html.twig', [
  1241.         ]);
  1242.     }
  1243.     /**
  1244.      * @Route(
  1245.      *     "/{_locale}/account/api/v1/ics/",
  1246.      *     name="get-api-ics",
  1247.      *     defaults={
  1248.      *          "_locale": "fi"
  1249.      *     },
  1250.      *     requirements={
  1251.      *          "_locale": "fi|sv|en"
  1252.      *     },
  1253.      *     methods={"GET"}
  1254.      * )
  1255.      * @param Request $request
  1256.      *
  1257.      * @return \Symfony\Component\HttpFoundation\Response
  1258.      * @throws TessituraClientException
  1259.      */
  1260.     public function downloadICSAction(Request $request)
  1261.     {
  1262.         $performanceId $request->get('performance');
  1263.         if (!$performanceId) {
  1264.             throw new \InvalidArgumentException();
  1265.         }
  1266.         /** @var ProductionRepository $productionRepository */
  1267.         $productionRepository $this->em->getRepository(Production::class);
  1268.         /** @var Performance $performance */
  1269.         $performance $this->client->request(Performances::get($performanceId))->denormalize(Performance::class);
  1270.         /** @var Production $production */
  1271.         $production $productionRepository->getProduction($performance->getId());
  1272.         if (!$production->getId()) {
  1273.             $production->setTitle($performance->getDescription());
  1274.         }
  1275.         $duration $performance->getDuration();
  1276.         $duration = (int)$duration ? (int)$duration 60;
  1277.         $start = new \DateTime();
  1278.         $start->setTimestamp($performance->getDate()->getTimestamp());
  1279.         $end = new \DateTime();
  1280.         $end->setTimestamp($performance->getDate()->getTimestamp());
  1281.         $end->add(new \DateInterval('PT' $duration 'M'));
  1282.         $location = new Location();
  1283.         $location->setName('Suomen kansallisooppera ja -baletti\, Helsinginkatu 58\, 00251 Helsinki\, Finland');
  1284.         $location->setUri('https://oopperabaletti.fi');
  1285.         $geo = new Geo();
  1286.         $geo->setLatitude(60.181608);
  1287.         $geo->setLongitude(24.929601);
  1288.         $calendarEvent = new CalendarEvent();
  1289.         $calendarEvent->setGeo($geo);
  1290.         $calendarEvent->setUid(md5($performance->getCode()));
  1291.         $calendarEvent->setLocations([
  1292.             $location
  1293.         ]);
  1294.         $calendarEvent->setStart($start);
  1295.         $calendarEvent->setSummary($production->getTitle());
  1296.         $calendarEvent->setEnd($end);
  1297.         $calendarEvent->setDescription($production->getStage());
  1298.         $calendar = new Calendar();
  1299.         $calendar->setMethod('REQUEST');
  1300.         $calendar->setTimezone(new \DateTimeZone('Europe/Helsinki'));
  1301.         $calendar->setProdId('-//FNOB//Ticketshop');
  1302.         $calendar->addEvent($calendarEvent);
  1303.         $calendarExport = new CalendarExport(new CalendarStream(), new Formatter());
  1304.         $calendarExport->addCalendar($calendar);
  1305.         $ics $calendarExport->getStream();
  1306.         $ics preg_replace('/^(DTSTART|DTEND)(.*)$/m''$1;TZID=Europe/Helsinki$2'$ics);
  1307.         $response = new Response($ics);
  1308.         $response->headers->set('Content-Type''text/calendar');
  1309.         return $response;
  1310.     }
  1311.     /**
  1312.      * @Route(
  1313.      *     "/{_locale}/account/api/v1/print/",
  1314.      *     name="post-api-print",
  1315.      *     defaults={
  1316.      *          "_locale": "fi"
  1317.      *     },
  1318.      *     requirements={
  1319.      *          "_locale": "fi|sv|en"
  1320.      *     },
  1321.      *     methods={"POST"}
  1322.      * )
  1323.      * @param Request $request
  1324.      * @param AccountService $accountService
  1325.      * @param ConstituentService $constituentService
  1326.      *
  1327.      * @return Response
  1328.      */
  1329.     public function printTicketAction(
  1330.         Request $request,
  1331.         AccountService $accountService,
  1332.         ConstituentService $constituentService,
  1333.     ) {
  1334.         $primaryEmail $constituentService->getConstituent()->getDefaultElectronicAddress();
  1335.         try {
  1336.             $accountService->reprintTickets(
  1337.                 $request->get('sublineItem'),
  1338.                 $primaryEmail->getId()
  1339.             );
  1340.         } catch (TessituraClientException $exception) {
  1341.             switch ($exception->getCode()) {
  1342.                 case TessituraClientException::TESSITURA_PENDING_PAH_REQUEST:
  1343.                     $message $this->translator->trans("upcoming_order_ticket_pending_error");
  1344.                     break;
  1345.                 default:
  1346.                     $message $this->translator->trans("upcoming_order_ticket_error");
  1347.                     break;
  1348.             }
  1349.             return new JsonResponse([
  1350.                 'success' => false,
  1351.                 'message' => $message,
  1352.                 'code' => $exception->getCode()
  1353.             ]);
  1354.         }
  1355.         return new JsonResponse([
  1356.             'success' => true,
  1357.             'message' => $this->translator->trans("upcoming_order_ticket_message") . ' ' $primaryEmail->getAddress()
  1358.         ]);
  1359.     }
  1360.     /**
  1361.      * @Route(
  1362.      *     "/{_locale}/account/api/v1/show/{subLineId}",
  1363.      *     name="get-api-show",
  1364.      *     defaults={
  1365.      *          "_locale": "fi"
  1366.      *     },
  1367.      *     requirements={
  1368.      *          "_locale": "fi|sv|en",
  1369.      *          "subLineId": "\d+",
  1370.      *     },
  1371.      * )
  1372.      * @param Request $request
  1373.      * @param $subLineId
  1374.      * @param AccountService $accountService
  1375.      * @param SerializerInterface $serializer
  1376.      * @return JsonResponse
  1377.      */
  1378.     public function getShowTicket(
  1379.         $subLineId,
  1380.         AccountService $accountService,
  1381.         SerializerInterface $serializer,
  1382.     ): JsonResponse {
  1383.         /*
  1384.         /** @var AccountItemPerformance[] $performances */
  1385.         try {
  1386.             $barcode $accountService->getTicketBarcode($subLineId);
  1387.         } catch (TessituraClientException $exception) {
  1388.             throw new NotFoundHttpException('No Barcode found');
  1389.         }
  1390.         if (empty($barcode)) {
  1391.             throw new NotFoundHttpException('No Barcode found');
  1392.         }
  1393.         $entityManager $this->getDoctrine()->getManager();
  1394.         /** @var MobileAppTicketShareRepository $repository */
  1395.         $repository $entityManager->getRepository(MobileAppTicketShare::class);
  1396.         $shared $repository->findBySublineId($subLineId);
  1397.         $sharedData $serializer->normalize($sharednull, ['groups' => ['json']]);
  1398.         $barcode array_merge([
  1399.             'shared_to' => $sharedData
  1400.         ], $barcode);
  1401.         return new JsonResponse($barcode);
  1402.     }
  1403.     /**
  1404.      * @Route(
  1405.      *     "/{_locale}/account/api/v1/return/",
  1406.      *     name="post-api-return",
  1407.      *     defaults={
  1408.      *          "_locale": "fi"
  1409.      *     },
  1410.      *     requirements={
  1411.      *          "_locale": "fi|sv|en"
  1412.      *     },
  1413.      *     methods={"POST"}
  1414.      * )
  1415.      * @param Request $request
  1416.      * @param AccountService $accountService
  1417.      *
  1418.      * @return Response
  1419.      */
  1420.     public function returnTicketAction(
  1421.         Request $request,
  1422.         AccountService $accountService,
  1423.         // Security $security,
  1424.     ) {
  1425.         /* Why? */
  1426.         /* $user = $security->getUser(); */
  1427.         $returnMessage $this->translator->trans("upcoming_return_done_message");
  1428.         try {
  1429.             $accountService->removeTicket(
  1430.                 $request->get('sublineItem')
  1431.             );
  1432.             //return new Response('<html><head></head><body>XXX</body></html>');
  1433.         } catch (\Exception $exception) {
  1434.             $this->logger->error("Exception while returning ticket (follows):");
  1435.             $this->logger->error(print_r($exceptiontrue));
  1436.             if ($exception instanceof TessituraClientException) {
  1437.                 $this->logger->error('Return: Exception while returning', [$exception]);
  1438.                 $this->cache->invalidateTags([$accountService->getCacheKey()]);
  1439.                 $returnMessage $this->translator->trans("upcoming_return_error");
  1440.                 return new JsonResponse([
  1441.                     'success' => false,
  1442.                     'message' => $returnMessage,
  1443.                     'code' => $exception->getCode()
  1444.                 ]);
  1445.             } elseif (
  1446.                 $exception instanceof CateringServiceException &&
  1447.                 $exception->getCode() === CateringServiceException::TABLE_ALL_REMOVED
  1448.             ) {
  1449.                 $returnMessage $this->translator->trans("upcoming_return_done_all_catering_removed_message");
  1450.             } elseif (
  1451.                 $exception instanceof CateringServiceException &&
  1452.                 $exception->getCode() === CateringServiceException::ITEMS_ALL_REMOVED
  1453.             ) {
  1454.                 $returnMessage $this->translator->trans("upcoming_return_done_all_catering_bookings_removed_message");
  1455.             } else {
  1456.                 $this->logger->error("Unknown exception type?");
  1457.                 $this->cache->invalidateTags([$accountService->getCacheKey()]);
  1458.                 throw $exception;
  1459.             }
  1460.         }
  1461.         if ($request->get('context') === 'me') {
  1462.             $twigResponse $this->meAction($request);
  1463.         } elseif ($request->get('context') === 'history') {
  1464.             $twigResponse $this->purchasesAction($request);
  1465.         } else {
  1466.             $twigResponse $this->upcomingAction($request);
  1467.         }
  1468.         return new JsonResponse([
  1469.             'success' => true,
  1470.             'message' => $returnMessage,
  1471.             'html' => $twigResponse->getContent(),
  1472.         ]);
  1473.     }
  1474.     /**
  1475.      * @Route(
  1476.      *     "/{_locale}/account/api/v1/load-order/",
  1477.      *     name="post-api-load-order",
  1478.      *     defaults={
  1479.      *          "_locale": "fi"
  1480.      *     },
  1481.      *     requirements={
  1482.      *          "_locale": "fi|sv|en"
  1483.      *     },
  1484.      *     methods={"POST"}
  1485.      * )
  1486.      * @param Request $request
  1487.      * @param CartService $cartService
  1488.      * @param RouterInterface $router
  1489.      *
  1490.      * @return Response
  1491.      */
  1492.     public function loadOrderAction(
  1493.         Request $request,
  1494.         CartService $cartService,
  1495.         RouterInterface $router,
  1496.         $recursion false
  1497.     ): JsonResponse {
  1498.         $url $router->generate('cart');
  1499.         try {
  1500.             $cartService->loadOrder($request->get('orderId'));
  1501.         } catch (TessituraClientException $exception) {
  1502.             switch ($exception->getCode()) {
  1503.                 case TessituraClientException::TESSITURA_ORDER_LOAD_CART_NOT_EMPTY:
  1504.                     if (!$recursion) {
  1505.                         try {
  1506.                             $cartService->emptyCart();
  1507.                             return $this->loadOrderAction($request$cartService$routertrue);
  1508.                         } catch (TessituraClientException $exception) {
  1509.                         }
  1510.                     }
  1511.                     $message $this->translator->trans("order_open_cart_error");
  1512.                     break;
  1513.                 case TessituraClientException::TESSITURA_ORDER_LOCKED:
  1514.                     $message $this->translator->trans("order_open_locked_error");
  1515.                     break;
  1516.                 default:
  1517.                     $message $this->translator->trans("order_open_general_error");
  1518.                     break;
  1519.             }
  1520.             return new JsonResponse([
  1521.                 'success' => false,
  1522.                 'message' => $message,
  1523.             ]);
  1524.         }
  1525.         return new JsonResponse([
  1526.             'success' => true,
  1527.             'redirect' => $url
  1528.         ]);
  1529.     }
  1530.     /**
  1531.      * @Route(
  1532.      *     "/{_locale}/account/me/",
  1533.      *     name="account-me",
  1534.      *     defaults={
  1535.      *          "_locale": "fi"
  1536.      *     },
  1537.      *     requirements={
  1538.      *          "_locale": "fi|sv|en"
  1539.      *     }
  1540.      * )
  1541.      * @param Request $request
  1542.      *
  1543.      * @return \Symfony\Component\HttpFoundation\Response
  1544.      * @throws TessituraClientException
  1545.      */
  1546.     public function meAction(
  1547.         Request $request,
  1548.         Security $security,
  1549.         TessituraUserProvider $userProvider,
  1550.         AccountService $accountService,
  1551.         ConstituentService $constituentService,
  1552.     ) {
  1553.         /* "New" local user check. If needed, user session is created and logged in to tessitura */
  1554.         $user $security->getUser();
  1555.         $session $request->getSession();
  1556.         if (!$session->has('tessitura_user') || !$session->has('tessitura_session')) {
  1557.             /** @var EntityUser $user */
  1558.             $sessionKey $session->has('tessitura_session')
  1559.                 ? $session->get('tessitura_session')
  1560.                 : $userProvider->createSessionKey();
  1561.             if (!$sessionKey) {
  1562.                 throw new \Exception('Failed to create Tessitura session');
  1563.             }
  1564.             $tessituraUser $userProvider->loadUsingConstituentInfo(
  1565.                 $sessionKey,
  1566.                 $user->getConstituentId(),
  1567.                 $user->getPhoneNumber(),
  1568.                 $user->getPostalCode()
  1569.             );
  1570.             if (!$tessituraUser) {
  1571.                 throw new \Exception('Failed to load Tessitura user');
  1572.             }
  1573.             $session->set('tessitura_user'$tessituraUser);
  1574.             $session->set('tessitura_session'$sessionKey);
  1575.         } else {
  1576.             $tessituraUser $session->get('tessitura_user');
  1577.         }
  1578.         
  1579.         $data $this->tessituraCacheService->get('account_me_' $user->getId());
  1580.         if (!$data) {
  1581.             $constituent $constituentService->getConstituent($tessituraUser->getConstituentId());
  1582.             $data = [
  1583.                 'badge' => $accountService->getConstituencyBadge(),
  1584.                 'firstname' => $constituent->getFirstName(),
  1585.                 'lastname' => $constituent->getLastName(),
  1586.                 'balance' => $accountService->getBalance(true),
  1587.                 'badges' => $accountService->getConstituencyBadges(),
  1588.                 'unpaidorders' => $accountService->getUnpaidOrders(),
  1589.                 'upcoming' => $accountService->getUpcoming(),
  1590.                 'sortupcoming' => $accountService->getSortUpcoming(),
  1591.             ];
  1592.             $this->tessituraCacheService->put('account_me_' $user->getId(), json_encode($data));
  1593.         } else {
  1594.             $data json_decode($datatrue);
  1595.         }
  1596.         
  1597.         return $this->render('page/account-me.html.twig'array_merge($data, [
  1598.             'user_data_api_url' => $this->generateUrl('get-user-data'),
  1599.             'wp_host' => $this->params->get('wp.domain')
  1600.         ]));
  1601.     }
  1602.     /**
  1603.      * @Route(
  1604.      *     "/{_locale}/account/purchases/",
  1605.      *     name="account-purchases",
  1606.      *     defaults={
  1607.      *          "_locale": "fi"
  1608.      *     },
  1609.      *     requirements={
  1610.      *          "_locale": "fi|sv|en"
  1611.      *     }
  1612.      * )
  1613.      * @param Request $request
  1614.      *
  1615.      * @return \Symfony\Component\HttpFoundation\Response
  1616.      * @throws TessituraClientException
  1617.      */
  1618.     public function purchasesAction(Request $request)
  1619.     {
  1620.         return $this->render('page/account-purchases.html.twig', [
  1621.         ]);
  1622.     }
  1623.     /**
  1624.      * @Route(
  1625.      *     "/{_locale}/account/viewing-rights/",
  1626.      *     name="account-viewing-rights",
  1627.      *     defaults={
  1628.      *          "_locale": "fi"
  1629.      *     },
  1630.      *     requirements={
  1631.      *          "_locale": "fi|sv|en"
  1632.      *     }
  1633.      * )
  1634.      * @param Request $request
  1635.      * @param AccountService $accountService
  1636.      *
  1637.      * @return \Symfony\Component\HttpFoundation\Response
  1638.      * @throws TessituraClientException
  1639.      */
  1640.     public function viewingRightsAction(Request $requestAccountService $accountService)
  1641.     {
  1642.         return $this->render('page/account-viewing-rights.html.twig', [
  1643.             'wp_host' => $this->params->get('wp.domain'),
  1644.             'streams' => array_merge([], array_map(function ($item) {
  1645.                 return $item->stream_id;
  1646.             }, $accountService->getPurchasedFeeProducts()))
  1647.         ]);
  1648.     }
  1649.     /**
  1650.      * @Route(
  1651.      *     "/{_locale}/account/upcoming/",
  1652.      *     name="account-upcoming",
  1653.      *     defaults={
  1654.      *          "_locale": "fi"
  1655.      *     },
  1656.      *     requirements={
  1657.      *          "_locale": "fi|sv|en"
  1658.      *     }
  1659.      * )
  1660.      * @param Request $request
  1661.      *
  1662.      * @return \Symfony\Component\HttpFoundation\Response
  1663.      */
  1664.     public function upcomingAction(Request $request)
  1665.     {
  1666.         return $this->render('page/account-upcoming.html.twig', [
  1667.         ]);
  1668.     }
  1669.     /**
  1670.      * @Route(
  1671.      *     "/{_locale}/account/upcoming-catering/{performanceId}/",
  1672.      *     name="account-upcoming-catering",
  1673.      *     defaults={
  1674.      *          "_locale": "fi"
  1675.      *     },
  1676.      *     requirements={
  1677.      *          "_locale": "fi|sv|en"
  1678.      *     }
  1679.      * )
  1680.      * @param Request $request
  1681.      *
  1682.      * @param $performanceId
  1683.      * @return \Symfony\Component\HttpFoundation\Response
  1684.      */
  1685.     public function upcomingCateringAction(Request $request$performanceId)
  1686.     {
  1687.         return $this->render('page/account-upcoming-catering.html.twig', [
  1688.             'performance_id' => $performanceId
  1689.         ]);
  1690.     }
  1691.     /**
  1692.      * @Route(
  1693.      *     "/{_locale}/account/favourites/",
  1694.      *     name="account-favourites",
  1695.      *     defaults={
  1696.      *          "_locale": "fi"
  1697.      *     },
  1698.      *     requirements={
  1699.      *          "_locale": "fi|sv|en"
  1700.      *     }
  1701.      * )
  1702.      * @param Request $request
  1703.      *
  1704.      * @return \Symfony\Component\HttpFoundation\Response
  1705.      */
  1706.     public function favouritesAction(Request $request)
  1707.     {
  1708.         return $this->render('page/account-favourites.html.twig', [
  1709.         ]);
  1710.     }
  1711. }