<?php
namespace App\EventSubscriber\Doctrine;
use App\Enum\ActivityLogCategoryEnum;
use App\Enum\ActivityLogMethodEnum;
use App\Manager\UserRegisterManager;
use App\Service\OrganizationUtils;
use App\Service\SegmentAPI;
use App\Traits\SentryNotifyTrait;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use Evo\Infrastructure\MappingORM\ActivityLog;
use Evo\Infrastructure\MappingORM\User;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Security;
class UserSubscriber implements EventSubscriber
{
use SentryNotifyTrait;
public const SERVICE_NAME = '[USER SUBSCRIBER] :: ';
private SegmentAPI $segment;
private UserPasswordEncoderInterface $encoder;
private OrganizationUtils $organizationUtils;
private UserRegisterManager $userRegisterManager;
private EntityManagerInterface $em;
private Security $security;
private RequestStack $requestStack;
public function __construct(
SegmentAPI $segmentAPI,
OrganizationUtils $organizationUtils,
UserPasswordEncoderInterface $encoder,
UserRegisterManager $userRegisterManager,
EntityManagerInterface $em,
Security $security,
RequestStack $requestStack
) {
$this->segment = $segmentAPI;
$this->organizationUtils = $organizationUtils;
$this->encoder = $encoder;
$this->userRegisterManager = $userRegisterManager;
$this->em = $em;
$this->security = $security;
$this->requestStack = $requestStack;
}
public function getSubscribedEvents(): array
{
return [
'prePersist',
'postPersist',
'preUpdate',
'postUpdate',
];
}
public function prePersist(LifecycleEventArgs $args): void
{
$entity = $args->getObject();
if (!$entity instanceof User) {
return;
}
if ('admin@digidom.pro' === $entity->getEmail()) {
throw new NotFoundHttpException('User can\'t be updated.');
}
$entity->setPhoneNumber(str_replace(' ', '', $entity->getPhoneNumber()));
$encoded = $this->encoder->encodePassword($entity, $entity->getPassword());
$entity->setPassword($encoded);
if (!$entity->getUniqId()) {
$entity->setUniqId(uniqid());
}
$this->userRegisterManager->initHashToken($entity);
}
public function postPersist(LifecycleEventArgs $args): void
{
/** @var User $entity */
$entity = $args->getObject();
if (!$entity instanceof User) {
return;
}
$this->updateContactOrganization($entity);
$this->segment->trackNewUser($entity);
$request = $this->requestStack->getCurrentRequest();
$token = $this->security->getToken();
$user = null;
if ($request instanceof Request) {
if ($token instanceof SwitchUserToken) {
$user = $token->getOriginalToken()->getUser();
} else {
$user = $this->security->getUser();
}
}
foreach ($entity->getOrganizations() as $organization) {
$activityLog = ActivityLog::create(
$user,
ActivityLogMethodEnum::CREATE,
'AJOUT USER ID: '.$entity->getId(),
ActivityLogCategoryEnum::USER,
[],
$organization,
null,
$entity->getFullName()
);
$this->em->persist($activityLog);
}
try {
$this->em->flush();
} catch (\Exception $e) {
$this->captureSentryException($e);
}
}
public function preUpdate(PreUpdateEventArgs $args): void
{
$entity = $args->getObject();
if (!$entity instanceof User) {
return;
}
foreach ($entity->getOrganizations() as $orga) {
$orga->addUser($entity);
$this->em->persist($orga);
}
$entityChanged = $args->getEntityChangeSet();
if (($args->hasChangedField('email') && 'admin@digidom.pro' === $args->getOldValue('email')) || 'admin@digidom.pro' === $entity->getEmail()) {
throw new NotFoundHttpException("User can't be updated");
}
$traits = $this->preUpdateSegmentTraits($args->getEntityChangeSet());
if ([] !== $traits) {
$traits['userId'] = $entity->getUniqId();
$this->segment->identify([
'userId' => $entity->getUniqId(),
'traits' => $traits,
]);
}
if (array_key_exists('password', $entityChanged)) {
$encodePassword = $this->encoder->encodePassword($entity, $entity->getPassword());
$entity->setPassword($encodePassword);
}
$entity->setPhoneNumber(str_replace(' ', '', $entity->getPhoneNumber()));
if (!$args->hasChangedField('resetToken')) {
$entity->setResetToken(null);
$entity->setResetTokenExpirationDate(null);
} else {
$this->segment->trackPasswordRequest($entity);
}
}
public function postUpdate(LifecycleEventArgs $args): void
{
/** @var User $entity */
$entity = $args->getObject();
if (!$entity instanceof User) {
return;
}
$this->em = $args->getObjectManager();
$uow = $this->em->getUnitOfWork();
$uow->computeChangeSets();
$changeset = $uow->getEntityChangeSet($entity);
if (isset($changeset['email']) || isset($changeset['phoneNumber'])) {
$this->updateContactOrganization($entity);
}
$organizations = $entity->getOrganizations();
foreach ($organizations as $organization) {
$this->segment->trackNewOrganization($organization, false);
}
if (isset($changeset['codeConfirmation'])) {
$this->segment->trackDoubleAuthentication($entity);
}
if (isset($changeset['firstname']) || isset($changeset['lastname'])) {
$oldFirstName = $changeset['firstname'][0] ?? '';
$oldLastName = $changeset['lastname'][0] ?? '';
$oldFullName = ('' !== $oldFirstName ? $oldFirstName : $entity->getFirstname()).' '.('' !== $oldLastName ? $oldLastName : $entity->getLastname());
$request = $this->requestStack->getCurrentRequest();
$token = $this->security->getToken();
$user = null;
if ($request instanceof Request) {
if ($token instanceof SwitchUserToken) {
$user = $token->getOriginalToken()->getUser();
} else {
$user = $this->security->getUser();
}
}
$activityLogChangeSet = [
'USER' => [
$oldFullName => $entity->getFullName(),
],
];
foreach ($entity->getOrganizations() as $organization) {
$activityLog = ActivityLog::create(
$user,
ActivityLogMethodEnum::UPDATE,
'MODIFICATION USER ID: '.$entity->getId(),
ActivityLogCategoryEnum::USER,
$activityLogChangeSet,
$organization,
null,
null
);
$this->em->persist($activityLog);
}
try {
$this->em->flush();
} catch (\Exception $e) {
$this->captureSentryException($e);
}
}
}
private function updateContactOrganization(User $user): void
{
if ([] === $user->getOrganizations()) {
return;
}
foreach ($user->getOrganizations() as $organization) {
$organization->setTelephone($user->getPhoneNumber());
$organization->setEmail($user->getEmail());
$this->em->persist($organization);
}
$orga = $user->getOrganizations()[0]->addUser($user);
$this->em->persist($orga);
$this->em->flush();
}
private function preUpdateSegmentTraits($changeSetList): array
{
$traits = [];
$mapping = [
'email' => 'email',
'phoneNumber' => 'phone',
'lastname' => 'lastname',
'firstname' => 'firstname',
'createdAt' => 'created_at',
'honorificPrefix' => 'gender',
];
foreach ($changeSetList as $property => $changeSet) {
if (array_key_exists($property, $mapping)) {
$traits[$mapping[$property]] = $changeSet[1];
}
}
if ([] !== $traits) {
$traits['idType'] = 'user';
}
return $traits;
}
}