<?php
namespace App\Controller;
use App\Event\ModeEvent;
use App\Form\UserAuthenticationType;
use App\Handler\LoginUserHandler;
use App\Manager\CmsManager;
use App\Manager\FavoritesManager;
use App\Model\Place\Favorite;
use App\Normalizer\UserPreferencesNormalizer;
use App\Service\Internal\NetworkService;
use App\Service\Internal\UserService;
use App\Utils\DeviceInfo;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Contracts\Translation\TranslatorInterface;
class UserController extends AbstractController
{
// Number of years between min/max years
public const DELTA_MINMAX_YEARS = 120;
public const NETWORK_TANEO = 15;
/** @var string */
protected $baseUrl;
/**
* WidgetController constructor.
*/
public function __construct()
{
//TODO: Temporaire, à supprimer quand la conf par réseau et environnement sera externalisée
$this->baseUrl = 'https://' . $_SERVER['SERVER_NAME'];
}
public function post(
Request $request,
TranslatorInterface $translator,
UserService $userService,
NetworkService $networkService,
CmsManager $cmsManager,
LoginUserHandler $loginUserHandler
): Response {
$error = '';
$data = [];
$isWidget = boolval($request->query->get('isWidget', false));
$session = $request->getSession();
$flashbag = $session->getFlashBag();
$phoneNumberConfig = $this->getParameter('form.phone_number_config');
$passwordConfig = $this->getParameter('form.password_config');
$profileConfig = $this->getParameter('form.profile_config');
$email = $request->get('email');
if ($request->isMethod('POST')) {
$token = $request->request->get('token');
if (!$this->isCsrfTokenValid('create_user', $token)) {
$flashbag->add('danger', $translator->trans('token.invalid'));
return $this->redirectToRoute('user_create', ['email' => $email, 'isWidget' => $isWidget]);
}
$data = array(
'firstname' => $request->request->get('firstname'),
'lastname' => $request->request->get('lastname'),
'email' => $email,
'phone' => $request->request->get('phone'),
'password' => $request->get('pass1'),
'hasWallet' => 0
);
if ($request->get('firstname') && !preg_match(
'/' . $profileConfig['name_regex'] . '/',
$request->get('firstname')
)) {
$error = $translator->trans('subscription.format_firstname_not_correct');
}
if ($request->get('lastname') && !preg_match(
'/' . $profileConfig['name_regex'] . '/',
$request->get('lastname')
)) {
$error = $translator->trans('subscription.format_lastname_not_correct');
}
if ($request->get('gender') !== null && $request->get('gender') !== 'UNKNOWN') {
$data['gender'] = $request->get('gender');
}
if ($request->get('phone')) {
$data['phone'] = $request->get('phone');
}
if ($request->get('birthdate')) {
$data['birthdate'] = $request->get('birthdate');
}
if ($this->getParameter('module_activation.ticketing_module')) {
if (empty($request->get('birthdate'))) {
$error = $translator->trans('ticketing.error_birthdate_not_specified');
}
}
if ($request->get('address')) {
$data['address'] = $request->get('address');
}
if ($request->get('postalCode')) {
$data['postalCode'] = strtoupper($request->get('postalCode'));
}
if ($request->get('city')) {
$data['city'] = $request->get('city');
}
if (mb_strlen($request->get('pass1')) < $passwordConfig['min']) {
$error = $translator->trans(
'subscription.password_min_characters_new_format',
['nbChars' => $passwordConfig['min']]
);
}
if ($request->get('pass1')) {
if ($passwordConfig['disable_password_complexity'] === false && !preg_match(
'/' . $passwordConfig['regex'] . '/',
$request->get('pass1')
)) {
$error = $translator->trans(
'subscription.password_min_characters_new_format',
['nbChars' => $passwordConfig['min']]
);
}
}
if ($request->get('pass1') != $request->get('pass2')) {
$error = $translator->trans('subscription.wrong_passwords');
}
if ($request->get('phone')) {
if (!preg_match($phoneNumberConfig['regex'], $request->get('phone'))) {
$error = $translator->trans('subscription.format_telephone_not_correct');
}
}
if (!$error) {
$wsResponse = $userService->create($data);
if ($wsResponse->status != 200) {
if (isset($wsResponse->body->message) && isset($wsResponse->errorMessage)) {
$error = $wsResponse->body->message . " " . $wsResponse->errorMessage;
} else {
$error = $translator->trans('messagesErrors.errorServer');
}
} else {
/** @var Session $session */
$currSession = $request->getSession();
$redirectionUrl = $currSession->get('redirection_after_subscription');
$credential = ['username' => $email, 'password' => $request->get('pass1')];
$loginUserHandler->loginUser($credential);
$network = $networkService->getNetwork();
$networkId = isset($network['networks'][0]['id']) ? $network['networks'][0]['id'] : null;
if ($redirectionUrl) {
if ($networkId == self::NETWORK_TANEO) {
$redirectionUrl = base64_encode(
$this->generateUrl("account.global.homepage", [], UrlGeneratorInterface::ABSOLUTE_URL)
);
}
//Clear redirection url in session
$currSession->set('redirection_after_subscription', null);
return $this->redirect(base64_decode($redirectionUrl));
} else {
$redirectionUrl = $request->query->get('redirectionUrl');
$route = "homepage";
if ($networkId == self::NETWORK_TANEO) {
$route = "account.global.homepage";
}
$redirect = !empty($redirectionUrl) ? $redirectionUrl : $this->generateUrl(
$route,
[],
UrlGeneratorInterface::ABSOLUTE_URL
);
return $this->redirect($redirect);
}
}
}
}
$pages = $cmsManager->getCmsPages();
$context = [
'error' => $error,
'menuPages' => $pages['menuPages'],
'footerPages' => $pages['footerPages'],
'items' => $pages['items'],
'baseUrl' => $this->baseUrl,
'data' => $data,
'delta_minmax_years' => self::DELTA_MINMAX_YEARS,
'email' => $email
];
$context['phoneNumberConfig'] = $phoneNumberConfig;
$context['passwordConfig'] = $passwordConfig;
if ($isWidget) {
return $this->render('modules/search-widget/account/createUser.html.twig', $context);
}
return $this->render('pages/account/createUser.html.twig', $context);
}
public function verifyEmail(
Request $request,
UserService $userService,
TranslatorInterface $translator,
CmsManager $cmsManager
): Response {
$isWidget = boolval($request->query->get('isWidget', false));
if (
$this->getParameter('module_activation.ticketing_module') &&
!$this->getParameter('module_activation.sim_module')
) {
if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
return $this->redirectToRoute('homepage');
}
}
/** @var Session $session */
$session = $request->getSession();
$flashbag = $session->getFlashBag();
if ($request->isMethod('POST')) {
$token = $request->request->get('token');
if (!$this->isCsrfTokenValid('verifyEmail', $token)) {
$flashbag->add('danger', $translator->trans('token.invalid'));
return $this->redirectToRoute(
'verify_email',
[
'redirectionUrl' => $request->get('redirectionUrl'),
'isWidget' => $isWidget
]
);
}
$email = trim($request->get('email'));
$wsResponse = $userService->emailValidate($email);
if ($wsResponse->status == 200) {
return $this->redirectToRoute('verify_email_code', array('email' => $email, 'isWidget' => $isWidget));
} else {
if (isset($wsResponse->body->message)) {
$flashbag->add('danger', $wsResponse->body->message);
}
}
} else {
// Save in session redirection url after subscription
$redirectionUrl = trim($request->get('redirectionUrl'));
/** @var Session $session */
$currSession = $request->getSession();
$currSession->set('redirection_after_subscription', $redirectionUrl);
}
$pages = $cmsManager->getCmsPages();
$context = [
'menuPages' => $pages['menuPages'],
'footerPages' => $pages['footerPages'],
'items' => $pages['items']
];
//If the user is logged in, we return it (in order to show the sidebar)
$wsResponse = $userService->myAccount();
if (!$wsResponse->isError()) {
$context['user'] = $wsResponse->body;
}
$context['baseUrl'] = $this->baseUrl;
if ($isWidget) {
return $this->render('modules/search-widget/account/verifyEmail.html.twig', $context);
}
return $this->render('pages/account/verifyEmail.html.twig', $context);
}
public function verifyEmailCode(
Request $request,
TranslatorInterface $translator,
UserService $userService,
CmsManager $cmsManager
): Response {
$isWidget = boolval($request->query->get('isWidget', false));
$email = trim($request->get('email'));
$code = trim($request->get('code'));
/** @var Session $session */
$session = $request->getSession();
$flashbag = $session->getFlashBag();
$token = $request->request->get('token');
if (
(
$request->isMethod('POST')
&& !empty($code) && $this->isCsrfTokenValid('verifyEmailCode', $token)
) ||
(
$request->isMethod('GET')
&& !empty($code)
)
) {
$wsResponse = $userService->codeValidate($email, $code);
if ($wsResponse->status == 200) {
if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
if ($isWidget) {
$redirectionUrl = $request->query->get('redirectionUrl');
return $this->redirect($redirectionUrl);
}
return $this->redirectToRoute('user_update_email', array('new_email' => $email));
} else {
return $this->redirectToRoute('user_create', array('email' => $email, 'isWidget' => $isWidget));
}
} else {
$flashbag->add('danger', $translator->trans('wrong_email_code'));
}
}
if ($request->isMethod('POST') && !$this->isCsrfTokenValid('verifyEmailCode', $token)) {
$flashbag->add('danger', $translator->trans('token.invalid'));
}
$pages = $cmsManager->getCmsPages();
$context = [
'baseUrl' => $this->baseUrl,
'menuPages' => $pages['menuPages'],
'footerPages' => $pages['footerPages'],
'items' => $pages['items'],
'email' => $email,
'code' => $code
];
if ($isWidget) {
return $this->render('modules/search-widget/account/verifyEmailCode.html.twig', $context);
}
return $this->render('pages/account/verifyEmailCode.html.twig', $context);
}
public function updateEmail(
Request $request,
UserService $userService,
TranslatorInterface $translator
): Response {
$session = $request->getSession();
$flashbag = $session->getFlashBag();
$wsResponse = $userService->myAccount();
$user = null;
if (!$wsResponse->isError()) {
$user = $wsResponse->body;
}
$data = ['email' => $request->get('new_email')];
$wsResponse = $userService->update($data, $user->getEmail());
if ($wsResponse->status != 200) {
$error = $wsResponse->status;
$flashbag->add('danger', $error);
} else {
$flashbag->add('success', $translator->trans('flash.update.success'));
return $this->redirectToRoute('account', array('user' => $user));
}
$context = [
'user' => $user,
'baseUrl' => $this->baseUrl,
'checkPassword' => true
];
return $this->render('pages/account/main.html.twig', $context);
}
public function resetPassword(
Request $request,
TranslatorInterface $translator,
UserService $userService,
CmsManager $cmsManager
): Response {
$error = '';
$isWidget = boolval($request->query->get('isWidget', false));
$email = $request->query->get('email', '');
/** @var Session $session */
$session = $request->getSession();
$flashbag = $session->getFlashBag();
$pages = $cmsManager->getCmsPages();
$context = [
'baseUrl' => $this->baseUrl,
'email' => $email,
'menuPages' => $pages['menuPages'],
'footerPages' => $pages['footerPages'],
'items' => $pages['items']
];
if ($request->isMethod('POST')) {
$email = urlencode($request->get('email'));
$wsResponse = $userService->resetPassword($email);
$flashbag->add(
'success',
$translator->trans('flash.password_reset.success') . $translator->trans('flash.password_reset.success_warning')
);
return $this->redirectToRoute('user_reset_password', ['isWidget' => $isWidget]);
}
if ($isWidget) {
return $this->render('modules/search-widget/account/resetPassword.html.twig', $context);
}
return $this->render('pages/account/resetPassword.html.twig', $context);
}
public function updatePassword(
Request $request,
TranslatorInterface $translator,
UserService $userService,
CmsManager $cmsManager,
LoginUserHandler $loginUserHandler
): Response {
$code = $request->get('code');
$email = $request->get('email');
$error = '';
$session = $request->getSession();
$flashbag = $session->getFlashBag();
$passwordConfig = $this->getParameter('form.password_config');
if ($request->isMethod('POST')) {
$password = $request->get('password');
$password2 = $request->get('password2');
if (
($password == $password2) &&
(mb_strlen($password) >= $passwordConfig['min']) &&
preg_match('/' . $passwordConfig['regex'] . '/', $password)
) {
$data = array(
'password' => $password,
'token' => $code
);
$wsResponse = $userService->updatePassword($email, $data);
if ($wsResponse->status == 200) {
$credential = ['username' => $email, 'password' => $password];
$loginUserHandler->loginUser($credential);
$flashbag->add('success', $translator->trans('flash.password_update.success'));
return $this->redirectToRoute('account');
} else {
$error = $wsResponse->body->message;
$flashbag->add('danger', $error);
return $this->redirectToRoute('user_reset_password');
}
} else {
if ($password != $password2) {
$flashbag->add('warning', $translator->trans('flash.password_update.not_match'));
} else {
if (mb_strlen($password) < $passwordConfig['min']) {
$flashbag->add(
'warning',
$translator->trans(
'flash.password_update.bad_length_new_format',
['nbChars' => $passwordConfig['min']]
)
);
} else {
if ($passwordConfig['disable_password_complexity'] === false && !preg_match(
'/' . $passwordConfig['regex'] . '/',
$password
)) {
$flashbag->add(
'warning',
$translator->trans(
'flash.password_update.bad_complexity_new_format',
['nbChars' => $passwordConfig['min']]
)
);
}
}
}
}
}
$pages = $cmsManager->getCmsPages();
$context = [
'code' => $code,
'email' => $email,
'baseUrl' => $this->baseUrl,
'menuPages' => $pages['menuPages'],
'footerPages' => $pages['footerPages'],
'items' => $pages['items']
];
$context['passwordConfig'] = $passwordConfig;
return $this->render('user/update_password.html.twig', $context);
}
public function login(
Request $request,
AuthenticationUtils $authenticationUtils,
CmsManager $cmsManager
): Response {
$error = $authenticationUtils->getLastAuthenticationError();
$origin = $request->get('origin', "sim");
$redirection = $request->get('redirectionUrl', "");
$isWidget = $request->get('isWidget', false);
$isModal = $request->get('isModal', false);
$pages = $cmsManager->getCmsPages();
$parameters['menuPages'] = $pages['menuPages'];
$parameters['footerPages'] = $pages['footerPages'];
$parameters['items'] = $pages['items'];
$parameters['redirectionUrl'] = $redirection;
$parameters['baseUrl'] = $this->baseUrl;
$parameters['isWidget'] = $isWidget;
$parameters['loginMode'] = $this->getParameter('global.login_mode');
$parameters = DeviceInfo::addDeviceParameters($parameters);
$session = $request->getSession();
$addToFavorites = $session->get('addToFavorites');
if (!empty($addToFavorites)) {
$parameters['addToFavorites'] = $addToFavorites;
$session->remove('addToFavorites');
}
if ($isModal && $isWidget) {
return $this->render('pages/account/loginFormContentModal.html.twig', $parameters);
}
if ($isWidget) {
return $this->render('pages/account/loginPage.html.twig', $parameters);
}
if ($error && "ticketing" === $origin) {
return $this->render('includes/ticketing/login-modal.html.twig', $parameters);
}
return $this->render('pages/account/userLogin.html.twig', $parameters);
}
public function userLogout(Request $request, TokenStorageInterface $tokenStorage): Response
{
$tokenStorage->setToken(null);
$request->getSession()->invalidate();
$response = new RedirectResponse($this->generateUrl('homepage'));
$response->headers->clearCookie('REMEMBERME');
$referer = $request->get('referer');
if (isset($referer)) {
return $this->redirect($referer);
}
return $response;
}
/**
* Display user information and update it if POST
*/
public function myAccountAction(
Request $request,
TranslatorInterface $translator,
UserService $userService,
NetworkService $networkService,
UserPreferencesNormalizer $userPreferencesNormalizer,
FavoritesManager $favoritesManager,
EventDispatcherInterface $eventDispatcher,
CmsManager $cmsManager
): Response {
if (
$this->getParameter('module_activation.ticketing_module')
&& !$this->getParameter('module_activation.sim_module')
) {
return $this->redirectToRoute('account');
}
$user = null;
$error = '';
/** @var Session $session */
$session = $request->getSession();
$flashbag = $session->getFlashBag();
if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
$wsResponse = $userService->myAccount();
if (!$wsResponse->isError()) {
$user = $wsResponse->body;
}
} else {
return $this->redirectToRoute('homepage');
}
$networkService->getNetwork();
$itineraryOptions = $networkService->getItineraryOptions();
$preferences = $userPreferencesNormalizer->normalize(
json_decode(json_encode($wsResponse->body->preferences), true),
['itineraryOptions' => $itineraryOptions]
);
if (!$wsResponse->isError()) {
$user = $wsResponse->body;
}
$includedModes = $request->get('includedModes');
$excludedModesPreferences = !empty($preferences['tripPlanner']['excludedModes']) ? $preferences['tripPlanner']['excludedModes'] : [];
$modeEvent = new ModeEvent($includedModes, $excludedModesPreferences);
$eventDispatcher->dispatch($modeEvent, ModeEvent::NAME);
$modes = $modeEvent->getDefaultModes();
if ($request->isMethod('POST')) {
$data = array(
'firstname' => $request->get('firstname'),
'lastname' => $request->get('lastname'),
'email' => urlencode($request->get('email')),
'phone' => $request->get('phone'),
'hasWallet' => 0
);
if ($request->get('old_password') && $request->get('new_password')) {
if ($request->get('confirm_password')
&& $request->get('new_password') !== $request->get('confirm_password')) {
$flashbag->add('danger', $translator->trans('subscription.wrong_passwords'));
return $this->redirectToRoute('account');
} else {
$data['password'] = array(
'old' => $request->get('old_password'),
'new' => $request->get('new_password')
);
}
}
if ($request->get('gender')) {
$data['gender'] = $request->get('gender');
}
if ($request->get('address')) {
$data['address'] = $request->get('address');
}
if ($request->get('postalCode')) {
$data['postalCode'] = $request->get('postalCode');
}
if ($request->get('city')) {
$data['city'] = $request->get('city');
}
$data['preferences']['tripPlanner'] = [
'bikeProfile' => $request->get('bikeProfile'),
'walkProfile' => $request->get('walkProfile'),
'wheelchairAccessible' => $request->get('wheelchairAccessible'),
'showAccessibility' => $request->get('showAccessibility'),
'excludedModes' => $modeEvent->getExcludedModes()
];
$wsResponse = $userService->update($data, urlencode($request->get('email')));
if ($wsResponse->status != 200) {
$error = $wsResponse->body->message;
$flashbag->add('danger', $error);
} else {
$flashbag->add('success', $translator->trans('flash.update.success'));
}
return $this->redirectToRoute('account');
}
// Validation
$data = $request->get('data', []);
// Get API data
$favorites = $favoritesManager->getPlaces($data, true);
$homeFavoriteId = '';
$workFavoriteId = '';
foreach ($favorites as $favorite) {
if ($favorite['picto'] === Favorite::PICTO_HOME) {
$homeFavoriteId = $favorite['favoriteId'];
} elseif ($favorite['picto'] === Favorite::PICTO_WORK) {
$workFavoriteId = $favorite['favoriteId'];
}
}
//Get CMS navigation to build
$pages = $cmsManager->getCmsPages();
$context = [
'user' => $user,
'preferences' => $preferences,
'modes' => $modes,
'favorites' => $favorites,
'homeFavoriteId' => $homeFavoriteId,
'workFavoriteId' => $workFavoriteId,
'menuPages' => $pages['menuPages'],
'footerPages' => $pages['footerPages'],
'items' => $pages['items'],
'baseUrl' => $this->baseUrl
];
return $this->render('pages/account/main.html.twig', $context);
}
public function delete(Request $request, TranslatorInterface $translator, UserService $userService): Response
{
$session = $request->getSession();
$flashbag = $session->getFlashBag();
$deleteForm = $this->createForm(
UserAuthenticationType::class,
[],
[
'action' => $this->generateUrl('user_delete'),
'method' => 'POST'
]
);
$deleteForm->handleRequest($request);
$data = $deleteForm->getData();
$params = ['deleteForm' => $deleteForm->createView()];
if ($deleteForm->isSubmitted() && $deleteForm->isValid()) {
$response = $userService->login($data);
if (Response::HTTP_OK !== $response->status) {
$flashbag->add('danger', $translator->trans('user.account_delete_authentication.invalid'));
return $this->render('pages/account/global/include/user_authentication_form.html.twig', $params);
}
$response = $userService->delete($data['username']);
if (Response::HTTP_OK !== $response->status) {
$flashbag->add('danger', $translator->trans('user.account_delete.error'));
return $this->render('pages/account/global/include/user_authentication_form.html.twig', $params);
}
if ($request->isXmlHttpRequest()) {
return new JsonResponse(['redirect' => $this->generateUrl('user_logout')]);
}
return $this->redirectToRoute('user_logout');
}
return $this->render('pages/account/global/include/user_authentication_form.html.twig', $params);
}
}