src/Controller/UserController.php line 339
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Entity\Company;
use App\Entity\Offer;
use App\Entity\Team;
use App\Entity\User;
use App\Form\ProfileType;
use App\Form\RegistrationType;
use App\Form\ResetType;
use App\Service\CompanyService;
use App\Service\DropboxService;
use App\Service\MailjetService;
use App\Service\UserService;
use App\Service\WidgetService;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Contracts\Translation\TranslatorInterface;
final class UserController extends AbstractController
{
public function __construct(
private readonly TranslatorInterface $translator,
private readonly EntityManagerInterface $em,
private readonly UserService $userService,
private readonly WidgetService $widgetService,
private readonly DropboxService $dropboxService
) {
}
/**
* This will never be executed: Symfony will intercept this first and handle the logout automatically.
* See logout in config/packages/security.yaml
*/
#[Route(path: '/logout', name: 'logout')]
public function logout(): void
{
// @codeCoverageIgnoreStart
throw new Exception('This should never be reached!');
// @codeCoverageIgnoreEnd
}
/**
* Display login page
* The login action is automatically executed by Symfony
*/
#[Route(path: '/login', name: 'login')]
public function login(AuthenticationUtils $helper): Response
{
// If already connected, redirect
if ($this->isGranted('ROLE_USER')) {
return $this->redirectToRoute('index');
}
return $this->render('user/login.html.twig', [
'last_username' => $helper->getLastUsername(),
'error' => $helper->getLastAuthenticationError(),
]);
}
/**
* Register a new user
*/
#[Route(path: '/register', name: 'register')]
public function register(
Request $request,
Session $session,
CompanyService $companyService
): RedirectResponse|Response {
// If already connected, redirect
if ($this->isGranted('ROLE_USER')) {
return $this->redirectToRoute('index');
}
$user = new User();
$form = $this->createForm(RegistrationType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// Then, create user
$this->userService->setPasswordForUser($user);
$user->setCompany($companyService->createCompany($request->request->get('registration_company')));
$user->setIsCompanyAdmin(true);
$user->setLocale($request->getLocale());
$this->em->persist($user);
$this->em->flush();
// Check for subscription redirect
$stripeId = $request->request->get('stripeId');
if (isset($stripeId) && $this->em->getRepository(Offer::class)->findOneBy(['stripeId' => $stripeId])) {
// @codeCoverageIgnoreStart
return $this->redirectToRoute('subscription_new', ['stripeId' => $stripeId]);
// @codeCoverageIgnoreEnd
}
$session->getFlashBag()->add('info', $this->translator->trans('flash.account.register_confirmed.info', ['%email%' => $user->getEmail()]));
return $this->redirectToRoute('login');
}
return $this->render('user/register.html.twig', [
'form' => $form->createView(),
'stripeId' => $request->query->get('stripeId')
]);
}
/**
* Resend confirmation email
*/
#[Route(path: '/resend_register/{email}', name: 'user_resend_register_confirmation')]
public function resendRegisterConfirmation(string $email, Session $session): RedirectResponse
{
$user = $this->em->getRepository(User::class)->findOneBy(['email' => $email]);
// If user exist and not already enabled
if (!$user || $user->getIsEnabled()) {
$session->getFlashBag()->add('warning', $this->translator->trans('flash.account.register_confirmed.warning', ['%email%' => $email]));
} else {
$this->userService->sendRegisterConfirmationEmail($user);
$session->getFlashBag()->add('info', $this->translator->trans('flash.account.register_confirmed.info', ['%email%' => $user->getEmail()]));
}
return $this->redirectToRoute('login');
}
/**
* Enable user account
*/
#[Route(path: '/register/{token}', name: 'user_register_confirmed')]
public function registerConfirmed(string $token, Session $session): RedirectResponse
{
$user = $this->em->getRepository(User::class)->findOneBy(['confirmationToken' => $token]);
// Invalid token
if (!$user) {
$session->getFlashBag()->add('danger', $this->translator->trans('flash.account.resetting.reset.token_danger'));
return $this->redirectToRoute('index');
}
// Reset token, enable account
$user->setConfirmationToken(null);
$user->setIsEnabled(true);
$this->em->flush();
$session->getFlashBag()->add('success', $this->translator->trans('flash.account.register_confirmed.success'));
// Log the user
$this->userService->registerUserSession($user);
return $this->redirectToRoute('thank_you');
}
/**
* Invite a new user to company
*/
#[Route(path: '/invite', name: 'user_invite')]
#[IsGranted('ROLE_USER')]
public function invite(
Request $request,
Session $session,
UrlGeneratorInterface $router
): RedirectResponse|Response {
$formDatas = $request->request->all();
// If no email, render form
if (empty($email = $formDatas['invitation_email'] ?? null)) {
return $this->render('user/invite.html.twig', [
'teams' => $this->em->getRepository(Team::class)->findLike()
]);
}
// If user already in a company
$user = $this->em->getRepository(User::class)->findOneBy(['email' => $email]);
if ($user && !empty($user->getCompany())) {
$session->getFlashBag()->add('danger', $this->translator->trans('flash.account.invite.danger'));
return $this->render('user/invite.html.twig', [
'teams' => $this->em->getRepository(Team::class)->findLike()
]);
}
// If user does not exist, create it
if (!$user) {
$user = new User();
$user->setEmail($email);
$user->setCompany($this->getUser()->getCompany());
$user->setIsCompanyAdmin($formDatas['invitation_isAdmin'] ?? false);
$user->setLocale($this->getUser()->getLocale());
$user->setIsEnabled(true);
/** @var Team $team */
foreach ($teams = $this->em->getRepository(Team::class)->findByIds($formDatas['invitation_teams'] ?? []) as $team) {
$user->addTeam($team);
}
$this->em->persist($user);
$this->em->flush();
$url = $router->generate('resetting_reset', ['token' => $user->getConfirmationToken(), 'type' => 'invite'], UrlGeneratorInterface::ABSOLUTE_URL);
} else {
// If user already exist but do not have a company yet
$this->userService->setConfirmationTokenForUser($user);
$this->em->flush();
$url = $router->generate('user_invite_confirmed', ['token' => $this->getUser()->getCompany()->getUniqueHash() . '-' . $user->getConfirmationToken()], UrlGeneratorInterface::ABSOLUTE_URL);
}
$this->userService->sendInviteEmail($user, $url);
$session->getFlashBag()->add('success', $this->translator->trans('flash.account.invite.success', ['%email%' => $user->getEmail()]));
return $this->redirectToRoute('company_index');
}
/**
* Confirm invitation link
*/
#[Route(path: '/invite/{token}', name: 'user_invite_confirmed')]
public function inviteConfirmed(string $token, Session $session): RedirectResponse
{
$token = explode('-', $token); // Get user token and company token
// If it seems to a valid token, go on
if (count($token) == 2) {
$user = $this->em->getRepository(User::class)->findOneBy(['confirmationToken' => $token[1]]);
// Invalid user token
if (!$user) {
$session->getFlashBag()->add('danger', $this->translator->trans('flash.account.resetting.reset.token_danger'));
} else {
$company = $this->em->getRepository(Company::class)->findOneBy(['uniqueHash' => $token[0]]);
// If valid company token, add link to user, reset user token
if ($company) {
$user->setCompany($company);
$user->setConfirmationToken(null);
$this->em->flush();
$session->getFlashBag()->add('success', $this->translator->trans('flash.account.invite_confirmed.success', ['%company%' => $company->getName()]));
// Log user
$this->userService->registerUserSession($user);
}
}
}
return $this->redirectToRoute('index');
}
/**
* Update user profile
*/
#[Route(path: '/profile/edit', name: 'user_edit')]
#[IsGranted('ROLE_USER')]
public function edit(Request $request, Session $session): RedirectResponse|Response
{
$form = $this->createForm(ProfileType::class, $this->getUser());
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// @codeCoverageIgnoreStart
// If "empty" Dropbox token
if (substr_count($form->getData()->getDropboxToken(), 'X') == 64) {
$this->getUser()->setDropboxToken(null);
}
// @codeCoverageIgnoreEnd
$currentPassword = $request->request->get('current_password');
$isPasswordValid = $this->userService->isPasswordValidForUser($this->getUser(), $currentPassword);
// If all passwords checker are valid, update password
if ($currentPassword != '' && $isPasswordValid) {
$this->userService->setPasswordForUser($this->getUser());
$session->getFlashBag()->add('success', $this->translator->trans('flash.account.update.success'));
} elseif ($currentPassword != '' && !$isPasswordValid) {
// If all passwords checker are not valid, display an error
$session->getFlashBag()->add('danger', $this->translator->trans('flash.account.update.danger'));
} else {
// Display a success message for other information than passwords
$session->getFlashBag()->add('success', $this->translator->trans('flash.account.update.success'));
}
$this->em->flush();
return $this->redirectToRoute('user_edit');
}
// If form is not valid (wrong passwords repeat for eg), display an error
if ($form->isSubmitted() && !$form->isValid()) {
$session->getFlashBag()->add('danger', $this->translator->trans('flash.account.update.danger'));
}
return $this->render('user/profile/edit.html.twig', [
'form' => $form->createView()
]);
}
/**
* Update user mailer subscription
*/
#[Route(path: '/profile/mailer', name: 'user_mailer_send')]
#[IsGranted('ROLE_USER')]
public function mailerSend(MailjetService $mailjet, Request $request): Response
{
$save = $request->request->get('save'); // Get form sent
// If form sent
if (isset($save)) {
$this->userService->updateMailerSettings($this->getUser(), $request->request->all('send'));
}
// Render form
return $this->render('user/profile/send.html.twig', [
'templates' => $mailjet->getAllNotRequired(),
'mailerSubscription' => json_decode($this->getUser()->getMailerSubscription(), true)
]);
}
/**
* Ask for reset password
*/
#[Route(path: '/resetting/request', name: 'resetting_request')]
public function resetRequest(Request $request, Session $session): RedirectResponse|Response
{
// If already connected, redirect
if ($this->isGranted('ROLE_USER')) {
return $this->redirectToRoute('index');
}
// If form sent
$email = $request->request->get('email');
if (isset($email)) {
// Check for existing user
$user = $this->em->getRepository(User::class)->findOneBy(['email' => $request->request->get('email')]);
// If user exist, create a reset request
if ($user) {
$this->userService->sendResettingResetEmail($user);
$session->getFlashBag()->add('success', $this->translator->trans('flash.account.resetting.request.success'));
return $this->redirectToRoute('login');
} else {
// If user not exist, display an error
$session->getFlashBag()->add('danger', $this->translator->trans('flash.account.resetting.request.danger'));
}
}
return $this->render('user/resetting/request.html.twig');
}
/**
* Reset password
*/
#[Route(path: '/resetting/reset/{token}/{type}', name: 'resetting_reset')]
public function resetReset(
string $token,
string $type,
Request $request,
Session $session
): RedirectResponse|Response {
$user = $this->em->getRepository(User::class)->findOneBy(['confirmationToken' => $token]);
// Invalid token
if (!$user) {
$session->getFlashBag()->add('danger', $this->translator->trans('flash.account.resetting.reset.token_danger'));
return $this->redirectToRoute('login');
}
$form = $this->createForm(ResetType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// Set new password
$user->setPlainPassword($form->get('plainPassword')->getData());
$this->userService->setPasswordForUser($user);
$user->setConfirmationToken(null); // Reset token
$this->em->flush();
// If token is for invite and enable account
if ($type == 'invite') {
// Log user
$this->userService->registerUserSession($user);
$session->getFlashBag()->add('success', $this->translator->trans('flash.account.invite_confirmed.success', ['%company%' => $user->getCompany()->getName()]));
return $this->redirectToRoute('index');
} else {
// If token is for reset
$session->getFlashBag()->add('success', $this->translator->trans('flash.account.update.success'));
}
return $this->redirectToRoute('login');
}
// If form is not valid (wrong passwords repeat), display an error
if ($form->isSubmitted() && !$form->isValid()) {
$session->getFlashBag()->add('danger', $this->translator->trans('flash.account.update.danger'));
}
// Render form
return $this->render('user/resetting/reset.html.twig', [
'form' => $form->createView(),
'token' => $token,
'type' => $type
]);
}
#[Route(path: '/thank_you', name: 'thank_you', methods: ['GET'])]
#[IsGranted('ROLE_USER')]
public function thankYou(): Response
{
// If user try to access this page w/ more than one widget
if (count($this->getUser()->getWidgets()) != 1) {
return $this->redirectToRoute('index');
}
return $this->render('user/thank_you.html.twig', [
'widget' => $this->getUser()->getWidgets()->first(), // Get first user widget
'snippet_path' => $this->widgetService->getSnippetPrefixRoute(),
'snippet_filename' => WidgetService::SNIPPET_FILENAME
]);
}
/**
* Get Dropbox token validity
*/
#[Route(path: '/dropbox_connect', name: 'dropbox_connect')]
#[IsGranted('ROLE_USER')]
public function dropboxConnectAjax(Request $request): JsonResponse
{
return new JsonResponse($this->dropboxService->dropboxConnect($request->query->get('token')));
}
/**
* Search active user LIKE email (ajax request for Select2EntityType)
*/
#[Route(path: '/search_active_ajax', name: 'user_searchActive_ajax', methods: ['GET'])]
#[IsGranted('ROLE_USER')]
public function searchActiveAjax(Request $request): JsonResponse
{
return new JsonResponse($this->em->getRepository(User::class)->getActiveUser($request->get('q')));
}
}