<?php
namespace App\Controller;
use App\Entity\AnonymousUserInfo;
use App\Entity\User;
use App\Event\UserRegisteredEvent;
use App\Event\UserTermsAndConditionsAcceptedEvent;
use App\Exception\InvalidEmailException;
use App\Kernel;
use App\Service\ConversionEventService;
use App\Service\FeatureLimitationsService;
use App\Service\MailService;
use App\Service\RegistrationService;
use App\Service\RouterHelperService;
use App\Service\SessionService;
use App\Service\SplittestingService;
use App\Service\UserService;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
use FOS\UserBundle\Controller\RegistrationController as FOSUserBundleRegistrationController;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Form\Factory\FactoryInterface;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Model\UserManagerInterface;
use FOS\UserBundle\Util\CanonicalFieldsUpdater;
use JanusHercules\DatawarehouseIntegration\Domain\Entity\BusinessEvent;
use JanusHercules\DatawarehouseIntegration\Domain\Service\BusinessEventDomainService;
use JanusHercules\JoboffererOnboarding\SubverticalFacade\JoboffererOnboardingFacade;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* The methods here are overrides of FOSUserBundle methods to extend their functionality as needed.
*/
class RegistrationController extends FOSUserBundleRegistrationController
{
private EventDispatcherInterface $eventDispatcher;
private FactoryInterface $formFactory;
private UserManagerInterface $userManager;
private BusinessEventDomainService $businessEventDomainService;
private SplittestingService $splittestingService;
private TranslatorInterface $translator;
private EntityManagerInterface $entityManager;
private RegistrationService $registrationService;
private UserService $userService;
private ConversionEventService $conversionEventService;
private RequestStack $requestStack;
private MailService $mailService;
private SessionService $sessionService;
private RouterHelperService $routerHelperService;
private LoggerInterface $logger;
private FeatureLimitationsService $featureLimitationsService;
private CanonicalFieldsUpdater $canonicalFieldsUpdater;
private ObjectPersisterInterface $wantedJobsPersister;
private ObjectPersisterInterface $recurrentJobsPersister;
private Kernel $kernel;
public function __construct(
EventDispatcherInterface $eventDispatcher,
FactoryInterface $formFactory,
UserManagerInterface $userManager,
TokenStorageInterface $tokenStorage,
BusinessEventDomainService $businessEventDomainService,
SplittestingService $splittestingService,
TranslatorInterface $translator,
EntityManagerInterface $entityManager,
RegistrationService $registrationService,
UserService $userService,
ConversionEventService $conversionEventService,
RequestStack $requestStack,
SessionService $sessionService,
RouterHelperService $routerHelperService,
LoggerInterface $logger,
FeatureLimitationsService $featureLimitationsService,
Kernel $kernel
) {
parent::__construct($eventDispatcher, $formFactory, $userManager, $tokenStorage);
$this->eventDispatcher = $eventDispatcher;
$this->formFactory = $formFactory;
$this->userManager = $userManager;
$this->businessEventDomainService = $businessEventDomainService;
$this->splittestingService = $splittestingService;
$this->translator = $translator;
$this->entityManager = $entityManager;
$this->registrationService = $registrationService;
$this->userService = $userService;
$this->conversionEventService = $conversionEventService;
$this->requestStack = $requestStack;
$this->routerHelperService = $routerHelperService;
$this->sessionService = $sessionService;
$this->logger = $logger;
$this->featureLimitationsService = $featureLimitationsService;
$this->kernel = $kernel;
}
public function changeEmailAddressAction(Request $request): Response
{
$newEmailAddress = $request->get('newEmailAddress');
$userId = $request->get('userId');
$response = new Response();
$handleResult = $this->userService->handleNewUserEmailAddressConfirmation(
$this->getUser()->getId(),
$userId,
$newEmailAddress
);
if ($handleResult > UserService::CHANGE_EMAIL_RESULT_SUCCESSFUL) {
$response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);
if ($handleResult === UserService::CHANGE_EMAIL_RESULT_ALREADY_IN_USE) {
$alertMessage = $this->translator->trans('errors.email_address_already_in_use.alert');
$mainContent = $this->translator->trans('errors.email_address_already_in_use.main_content');
} else {
$alertMessage = $this->translator->trans('errors.general.alert');
$mainContent = $this->translator->trans('errors.general.main_content');
}
return $this->render(
'errors/general_error.html.twig',
[
'alert_message' => $alertMessage,
'main_content_message' => $mainContent
], $response
);
}
$response->setStatusCode(Response::HTTP_OK);
return $this->render('default/new_email_confirmation.html.twig', [], $response);
}
/** @throws Exception */
public function registerAction(Request $request): Response
{
/** @var User $user */
$user = $this->getUser();
if ($user instanceof User) {
$response = new Response();
$response->setStatusCode(Response::HTTP_FORBIDDEN);
return $this->render('errors/already_logged_in.html.twig', [], $response);
}
$assignedRegistrationSplittestingBucket = 0;
$user = $this->registrationService->createRudimentaryUser();
$response = $this->registrationService->dispatchRegistrationInitializedEvent($request, $user);
if (!is_null($response)) {
return $response;
}
$form = $this->formFactory->createForm();
$form->setData($user);
try {
$form->handleRequest($request);
} catch (InvalidEmailException $e) {
$this->addFlash('danger', $this->translator->trans('registration.flash.email_invalid'));
$response = $this->registrationService->handleRegistrationFailure($request, $form);
if (!is_null($response)) {
return $response;
}
}
$role = $request->get('createWithRole');
if ($form->isSubmitted()) {
$captchaIsValid = $this->registrationService->captchaIsValid($request);
if (!$captchaIsValid) {
$this->addFlash('danger', $this->translator->trans('registration.flash.captcha_invalid'));
} else {
if (!$form->isValid()) {
if (count($form->getErrors()) > 0) {
foreach ($form->getErrors() as $error) {
if ($error->getMessageTemplate() === 'fos_user.username.already_used') {
if (!is_null($user)) {
/** @var User $foundUser */
if (!empty($foundUser = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $user->getEmail()]))) {
if (!$foundUser->isEnabled()) {
if ($foundUser->getRoles()[0] === $role) {
$this->registrationService->checkEmailAddressValidity($user);
$user = $foundUser;
if ($role === User::ROLE_NAME_JOBSEEKER) {
$this->registrationService->addRequestProfileDataToJobseekerProfile($request, $user);
}
$event = new FormEvent($form, $request);
$createWithRole = $form['createWithRole']->getData();
if ($createWithRole !== User::ROLE_NAME_JOBSEEKER && $createWithRole !== User::ROLE_NAME_JOBOFFERER) {
$this->logger->warning("Invalid createWithRole {$createWithRole}, falling back to '" . User::ROLE_NAME_JOBSEEKER . "'.");
$createWithRole = User::ROLE_NAME_JOBSEEKER;
}
$user->addRole($createWithRole);
$user->setEnabled(false);
$token = $user->getConfirmationToken();
if ($this->sessionService->hasSessionAnonymousUserInfo($this->requestStack->getSession())) {
$anonymousUserInfo = $this->entityManager->find(AnonymousUserInfo::class, $this->sessionService->getAnonymousUserInfo($this->requestStack->getSession()));
$anonymousUserInfo->setToken($token);
$this->entityManager->persist($anonymousUserInfo);
$this->entityManager->flush();
}
$this->registrationService->sendConfirmationMailAndGetMessage($user);
$this->requestStack->getSession()->set(
'fos_user_send_confirmation_email/email',
$user->getEmail()
);
if ($user->hasRole(User::ROLE_NAME_JOBOFFERER)) {
if (JoboffererOnboardingFacade::userMustUseNewOnboardingFlow($user)) {
$url = $this->routerHelperService->generate(
'janus_aurora.jobofferer_onboarding.wait_for_activation',
['email' => $user->getEmail()],
);
} else {
$url = $this->routerHelperService->generate(
'fos_user_registration_check_email_jobofferer'
);
}
} elseif ($user->hasRole(User::ROLE_NAME_JOBSEEKER)) {
$url = $this->routerHelperService->generate('fos_user_registration_check_email_jobseeker');
} else {
$url = $this->routerHelperService->generate('fos_user_registration_check_email');
}
$event->setResponse(new RedirectResponse($url));
$this->eventDispatcher->dispatch(new FilterUserResponseEvent($user, $request, $event->getResponse()), FOSUserEvents::REGISTRATION_COMPLETED);
if ($role === User::ROLE_NAME_JOBSEEKER) {
$this->splittestingService->handleGoalReached(
SplittestingService::TEST_ID_JOBSEEKER_REGISTRATION_FORM_ORDER,
$event->getResponse(),
$request,
$user
);
}
return $event->getResponse();
}
$response = $this->registrationService->handleRegistrationFailure($request, $form, null);
if (!is_null($response)) {
return $response;
}
}
}
}
}
}
}
}
if ($form->isValid()) {
$this->registrationService->checkEmailAddressValidity($user);
if ($role === User::ROLE_NAME_JOBSEEKER) {
$this->registrationService->addRequestProfileDataToJobseekerProfile($request, $user);
}
$event = new FormEvent($form, $request);
$this->eventDispatcher->dispatch($event, FOSUserEvents::REGISTRATION_SUCCESS);
$this->userManager->updateUser($user);
if (null === $response = $event->getResponse()) {
$url = $this->generateUrl('fos_user_registration_confirmed');
$response = new RedirectResponse($url);
}
$this->eventDispatcher->dispatch(new FilterUserResponseEvent($user, $request, $response), FOSUserEvents::REGISTRATION_COMPLETED);
if ($form->get('acceptedTermsAndConditions')) {
$this->eventDispatcher->dispatch(
new UserTermsAndConditionsAcceptedEvent(
$user
)
);
}
$this->eventDispatcher->dispatch(
new UserRegisteredEvent($user),
UserRegisteredEvent::class
);
if ($role === User::ROLE_NAME_JOBSEEKER) {
$this->splittestingService->handleGoalReached(
SplittestingService::TEST_ID_JOBSEEKER_REGISTRATION_FORM_ORDER,
$response,
$request,
$user
);
}
return $response;
}
$response = $this->registrationService->handleRegistrationFailure($request, $form, null);
if (!is_null($response)) {
return $response;
}
}
}
$response = new Response();
if ($role !== User::ROLE_NAME_JOBOFFERER) {
$assignedRegistrationSplittestingBucket = $this->splittestingService->initiateSplittesting(
SplittestingService::TEST_ID_JOBSEEKER_REGISTRATION_FORM_ORDER,
$response
);
}
if ($request->get('away') === 'true') {
$this->businessEventDomainService->writeNewEvent(BusinessEvent::EVENT_TYPE_LINK_FROM_EBAY_AWAY_MESSAGE_CLICKED, $user);
}
if ($role === User::ROLE_NAME_JOBOFFERER) {
$this->conversionEventService->initiateGoalReachedConversionTracking(null, ConversionEventService::CAMPAIGN_ID_REGISTRATION_REFERER, 0, null, $request, null, json_encode(['referer' => $request->headers->get('referer')]));
}
if ($role === User::ROLE_NAME_JOBSEEKER) {
$this->conversionEventService->initiateGoalReachedConversionTracking(null, ConversionEventService::CAMPAIGN_ID_REGISTRATION_REFERER, 1, null, $request, null, json_encode(['referer' => $request->headers->get('referer')]));
}
$this->conversionEventService->initiateGoalReachedConversionTrackingBasedOnRequest($request);
return $this->render(
'@FOSUser/Registration/register.html.twig',
[
'form' => $form->createView(),
'createWithRole' => $role,
'assignedRegistrationSplittestingBucket' => $assignedRegistrationSplittestingBucket
],
$response
);
}
/** @throws Exception */
public function confirmedAction(Request $request): Response
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
/** @var User $user */
$user = $this->getUser();
$this->registrationService->handleUserAccountConfirmation($request, $user);
if ($this->featureLimitationsService->userMustBeForwardedToDataImport($user) !== -1) {
return $this->redirectToRoute('account.profiles.check_recruit_dl', ['usersId' => $user->getId()]);
} else {
if ($user->isJobofferer()) {
// Because our test setup currently depends on a very specific process to create a jobofferer test actor
// (see Tests\Application\TestCase::getActivatedJoboffererActor), we do not (yet) send the client to the
// new Jobofferer Onboarding flow.
if ($this->kernel->getEnvironment() === 'test') {
return $this->redirectToRoute('account.profiles.index', ['isFirstEdit' => true]);
}
if (JoboffererOnboardingFacade::userMustUseNewOnboardingFlow($user)) {
return $this->redirectToRoute('janus_aurora.jobofferer_onboarding.start');
} else {
return $this->redirectToRoute('account.profiles.index', ['isFirstEdit' => true]);
}
}
return $this->redirectToRoute('janus_hercules.jobseeker_registration.presentation.index', ['step' => 0, 'firstEdit' => true]);
}
}
public function confirmAction(Request $request, $token): Response
{
$userManager = $this->userManager;
$user = $userManager->findUserByConfirmationToken($token);
// User not found? Instead of showing an error, redirect to homepage.
if (is_null($user)) {
$url = $this->generateUrl('homepage');
return new RedirectResponse($url);
}
// User found and already enabled - aka the user has repeatedly clicked on the activation link.
// In this case, log the user in.
if ($user->isEnabled()) {
$this->userService->login($request, $user);
return new RedirectResponse($this->generateUrl('account.conversations.index_router'));
}
// None of the above? That's the default case and we activate the account.
return $this->registrationService->confirmUserRegistration($request, $user);
}
}