src/Controller/Api/ProfileController.php line 64

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Api;
  3. use App\Entity\Core\AccountDeleted;
  4. use App\Entity\Core\Agencies;
  5. use App\Entity\Core\AgenciesHasUsers;
  6. use App\Entity\Core\MobileIdentifiers;
  7. use App\Entity\Cvs\CandidatesHasComments;
  8. use App\Entity\Cvs\CandidatesHasLikes;
  9. use App\Entity\Cvs\CandidatesHasStatistics;
  10. use App\Entity\Messenger\Groups;
  11. use App\Entity\Messenger\Messages;
  12. use App\Services\Dossiers;
  13. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
  14. use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
  15. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  16. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  17. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  18. use Symfony\Component\HttpFoundation\Request;
  19. use Symfony\Component\HttpFoundation\Response;
  20. use Symfony\Component\Routing\Annotation\Route;
  21. use Symfony\Component\HttpFoundation\JsonResponse;
  22. use Doctrine\ORM\EntityManagerInterface;
  23. use Knp\Component\Pager\PaginatorInterface;
  24. use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
  25. use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
  26. use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
  27. use Symfony\Component\Form\Extension\Core\Type\EmailType;
  28. use Symfony\Component\Form\Extension\Core\Type\HiddenType;
  29. use Symfony\Component\Form\Extension\Core\Type\NumberType;
  30. use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  31. use Symfony\Component\Form\Extension\Core\Type\TextareaType;
  32. use Symfony\Component\Form\Extension\Core\Type\TextType;
  33. use Symfony\Component\HttpFoundation\Session\Session;
  34. use Symfony\Component\HttpFoundation\File\File;
  35. use Symfony\Component\HttpFoundation\File\UploadedFile;
  36. use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
  37. class ProfileController extends AbstractController
  38. {
  39.     private $em;
  40.     private $paginator;
  41.     private $dossier;
  42.     private $passwordEncoder;
  43.     public function __construct(EntityManagerInterface $em,
  44.                                 Dossiers $dossier,
  45.                                 PaginatorInterface $paginator,
  46.                                 UserPasswordEncoderInterface $passwordEncoder,
  47.     ) {
  48.         $this->em $em;
  49.         $this->paginator $paginator;
  50.         $this->dossier $dossier;
  51.         $this->passwordEncoder $passwordEncoder;
  52.     }
  53.     /**
  54.      * Informations de l'utilisateur.
  55.      * @param Request $request
  56.      * @return JsonResponse
  57.      */
  58.     public function info(Request $request)
  59.     {
  60.         $user $this->getUser();
  61.         $language "en";
  62.         if($user->getLanguage() !== null) {
  63.             $language $user->getLanguage();
  64.         }
  65.         $avatar "";
  66.         if($user->getImage() !== null && $user->getImage()->getName() !== null) {
  67.             $avatar $user->getImageBase64();
  68.         }
  69.         // ════════════════════════════════════════════════════════════════
  70.         //  RECRUTEUR — auto-création / réparation de l'agence et de l'AHU
  71.         // ════════════════════════════════════════════════════════════════
  72.         if($user->getTypeAccount() == "enterprise") {
  73.             // CAS 1 : pas de currentAgency → on crée tout (agency + AHU)
  74.             if($user->getCurrentAgency() === null) {
  75.                 $agency = new Agencies();
  76.                 $agency->setEmail($user->getEmail());
  77.                 $agency->setFirst(true);
  78.                 $agency->setValide(false);
  79.                 $agency->setPremium(false);
  80.                 $agency->setCreatedAt(new \DateTime("now"));
  81.                 $agency->setUpdatedAt(new \DateTime("now"));
  82.                 $agency->setPourcentCommission(0);
  83.                 $agency->setPourcentCommissionBank(0);
  84.                 $agency->setNoCommission(false);
  85.                 $agency->setCommissionCentimesBank(0);
  86.                 $agency->setLimitedUsers(1);
  87.                 $agency->setLimitedCourses(10);
  88.                 $agency->setLimitedQcm(10);
  89.                 $agency->setLimitedQcmApplication(10);
  90.                 $agency->setMultipleInscription(false);
  91.                 $agency->setStripe(false);
  92.                 $agency->setDemonstration(false);
  93.                 $this->em->persist($agency);
  94.                 $this->em->flush();
  95.                 $user->setCurrentAgency($agency);
  96.                 $this->em->persist($user);
  97.                 $this->em->flush();
  98.                 $ahu = new AgenciesHasUsers();
  99.                 $ahu->setAgency($agency);
  100.                 $ahu->setUser($user);
  101.                 $ahu->setAdmin(false);
  102.                 $this->em->persist($ahu);
  103.                 $this->em->flush();
  104.             }
  105.             // CAS 2 : l'user a une currentAgency mais pas d'AHU → on crée l'AHU manquant
  106.             // (cas des comptes pré-existants à la refonte RC)
  107.             else {
  108.                 $existingAhu $this->em->getRepository(AgenciesHasUsers::class)->findOneBy([
  109.                     'user'   => $user,
  110.                     'agency' => $user->getCurrentAgency(),
  111.                 ]);
  112.                 if (!$existingAhu) {
  113.                     $ahu = new AgenciesHasUsers();
  114.                     $ahu->setAgency($user->getCurrentAgency());
  115.                     $ahu->setUser($user);
  116.                     $ahu->setAdmin(false);
  117.                     $this->em->persist($ahu);
  118.                     $this->em->flush();
  119.                 }
  120.             }
  121.         }
  122.         // ── Flags CV (candidats uniquement, sinon false) ──
  123.         $cvGenerated false;
  124.         $cvUploaded  false;
  125.         if ($user->getTypeAccount() !== 'enterprise') {
  126.             $candidate $user->getCandidate();
  127.             if ($candidate) {
  128.                 // CV généré : un cv_pdf_path a été enregistré par CvGeneratorController::generate
  129.                 $cvGenerated = !empty($candidate->getCvPdfPath());
  130.                 // CV uploadé (PDF original envoyé via analyze/upload) : image Vich renseignée
  131.                 $cvUploaded  $candidate->getImage() !== null
  132.                     && $candidate->getImage()->getName() !== null;
  133.             }
  134.         }
  135.         return new JsonResponse([
  136.             'id'  => $user->getId(),
  137.             'avatar' => $avatar ?? '',
  138.             'email' => $user->getEmail(),
  139.             'roles' => $user->getRoles(),
  140.             'typeAccount' => $user->getTypeAccount() ?? 'candidate',
  141.             'name' => $user->getName() ?? '',
  142.             'lastname' => $user->getLastname() ?? '',
  143.             'fullName' => '',
  144.             'language' => $language ?? 'en',
  145.             'first' => $user->isFirst() ?? 1,
  146.             'enabled' => $user->isEnabled() ?? 0,
  147.             'verification' => $user->isVerification() ?? 0,
  148.             'motifverification' => $user->getMotif() ?? '',
  149.             'premium' => $user->isPremium() ?? 0,
  150.             'cvGenerated' => $cvGenerated,
  151.             'cvUploaded'  => $cvUploaded,
  152.         ]);
  153.     }
  154.     /**
  155.      * Mettre le profil de l'utilisateur - POST.
  156.      * @param Request $request
  157.      * @return JsonResponse
  158.      */
  159.     public function update(Request $request): JsonResponse
  160.     {
  161.         if (!$request->isMethod('POST')) {
  162.             return new JsonResponse(['error' => 'Method not allowed'], 405);
  163.         }
  164.         $user $this->getUser();
  165.         if (!$user) {
  166.             return new JsonResponse(['error' => 'Unauthorized'], 401);
  167.         }
  168.         $data $request->toArray();
  169.         $user->setName($data['name']);
  170.         $user->setLastname($data['lastname']);
  171.         if (!empty($data['avatar64'])) {
  172.             try {
  173.                 $imageInfo $this->detectImageTypeFromBase64($data['avatar64']);
  174.                 // Décoder le base64
  175.                 $decodedImage base64_decode($imageInfo['data']);
  176.                 if ($decodedImage === false) {
  177.                     return new JsonResponse(['error' => 'Invalid base64 image'], 400);
  178.                 }
  179.                 // Créer un fichier temporaire avec un nom unique
  180.                 $tmpFilePath sys_get_temp_dir() . '/' uniqid('avatar_'true) . '.' $imageInfo['extension'];
  181.                 // Écrire l'image décodée dans le fichier temporaire
  182.                 $bytesWritten file_put_contents($tmpFilePath$decodedImage);
  183.                 if ($bytesWritten === false) {
  184.                     return new JsonResponse(['error' => 'Failed to write temporary file'], 500);
  185.                 }
  186.                 // ✅ IMPORTANT : Créer un objet UploadedFile (pas File) pour VichUploader
  187.                 $uploadedFile = new UploadedFile(
  188.                     $tmpFilePath,                    // Chemin du fichier temporaire
  189.                     'avatar.' $imageInfo['extension'], // Nom original du fichier
  190.                     $imageInfo['mimeType'],          // Type MIME
  191.                     null,                             // Erreur (null = pas d'erreur)
  192.                     true                              // test mode = true (fichier temporaire)
  193.                 );
  194.                 // Assigner le fichier à l'entité via VichUploader
  195.                 $user->setImageFile($uploadedFile);
  196.                 // ✅ IMPORTANT : Mettre à jour le timestamp pour forcer VichUploader à détecter le changement
  197.                 $user->setUpdatedAt(new \DateTimeImmutable());
  198.             } catch (\Exception $e) {
  199.                 // Nettoyer le fichier temporaire en cas d'erreur
  200.                 if (isset($tmpFilePath) && file_exists($tmpFilePath)) {
  201.                     @unlink($tmpFilePath);
  202.                 }
  203.                 return new JsonResponse(['error' => 'Upload failed: ' $e->getMessage()], 500);
  204.             }
  205.         }
  206.         if($user->getTypeAccount() == "enterprise") {
  207.             $user->setFirst(false);
  208.         }
  209.         $this->em->persist($user);
  210.         $this->em->flush();
  211.         if (isset($tmpFilePath) && file_exists($tmpFilePath)) {
  212.             @unlink($tmpFilePath);
  213.         }
  214.         // Nettoyer le fichier temporaire si créé
  215.         if (isset($tmpFilePath) && file_exists($tmpFilePath)) {
  216.             @unlink($tmpFilePath);
  217.         }
  218.         return new JsonResponse(['success' => true]);
  219.     }
  220.     /**
  221.      * Mettre à jour le language
  222.      * @param Request $request
  223.      * @return JsonResponse
  224.      */
  225.     public function updateLanguage(Request $request): JsonResponse
  226.     {
  227.         if (!$request->isMethod('POST')) {
  228.             return new JsonResponse(['error' => 'Method not allowed'], 405);
  229.         }
  230.         $user $this->getUser();
  231.         if (!$user) {
  232.             return new JsonResponse(['error' => 'Unauthorized'], 401);
  233.         }
  234.         $data $request->toArray();
  235.         $user->setLanguage($data['language']);
  236.         $this->em->persist($user);
  237.         $this->em->flush();
  238.         return new JsonResponse(['language' => $user->getLanguage(), 'success' => true]);
  239.     }
  240.     /**
  241.      * Mettre à jour le language
  242.      * @param Request $request
  243.      * @return JsonResponse
  244.      */
  245.     public function updateVerification(Request $request): JsonResponse
  246.     {
  247.         if (!$request->isMethod('POST')) {
  248.             return new JsonResponse(['error' => 'Method not allowed'], 405);
  249.         }
  250.         $user $this->getUser();
  251.         if (!$user) {
  252.             return new JsonResponse(['error' => 'Unauthorized'], 401);
  253.         }
  254.         $data $request->toArray();
  255.         $user->setVerification(false);
  256.         $user->setMotif($data['motif']);
  257.         $this->em->persist($user);
  258.         $this->em->flush();
  259.         return new JsonResponse(['success' => true]);
  260.     }
  261.     /**
  262.      * Mettre à jour le token de notifications.
  263.      * @param Request $request
  264.      * @return JsonResponse
  265.      */
  266.     public function updateNotifications(Request $request): JsonResponse
  267.     {
  268.         if (!$request->isMethod('POST')) {
  269.             return new JsonResponse(['error' => 'Method not allowed'], 405);
  270.         }
  271.         $user $this->getUser();
  272.         if (!$user) {
  273.             return new JsonResponse(['error' => 'Unauthorized'], 401);
  274.         }
  275.         $data $request->toArray();
  276.         if (!isset($data['identifiant']) || empty($data['identifiant'])) {
  277.             return new JsonResponse(['error' => 'Missing identifiant'], 400);
  278.         }
  279.         $mobileIdentifiers $this->em->getRepository(MobileIdentifiers::class)->findOneBy(['user' => $user'identifiant' => $data['identifiant']]);
  280.         if ($mobileIdentifiers === null) {
  281.             $mobileIdentifiers = new MobileIdentifiers();
  282.             $mobileIdentifiers->setUser($user);
  283.             $mobileIdentifiers->setIdentifiant($data['identifiant']);
  284.             $mobileIdentifiers->setDescription("expo_token_notifications");
  285.             $this->em->persist($mobileIdentifiers);
  286.             $this->em->flush();
  287.             return new JsonResponse([
  288.                 'token' => $mobileIdentifiers->getIdentifiant(),
  289.                 'success' => true,
  290.                 'created' => true
  291.             ], 201);
  292.         }
  293.         return new JsonResponse([
  294.             'token' => $mobileIdentifiers->getIdentifiant(),
  295.             'success' => true,
  296.             'created' => false
  297.         ], 200);
  298.     }
  299.     /**
  300.      * Supprimer les notifications.
  301.      * @param Request $request
  302.      * @return JsonResponse
  303.      */
  304.     public function deleteNotifications(Request $request): JsonResponse
  305.     {
  306.         if (!$request->isMethod('DELETE')) {
  307.             return new JsonResponse(['error' => 'Method not allowed'], 405);
  308.         }
  309.         $user $this->getUser();
  310.         if (!$user) {
  311.             return new JsonResponse(['error' => 'Unauthorized'], 401);
  312.         }
  313.         $data $request->toArray();
  314.         // Vérifier que l'identifiant est présent
  315.         if (!isset($data['identifiant']) || empty($data['identifiant'])) {
  316.             return new JsonResponse(['error' => 'Missing identifiant'], 400);
  317.         }
  318.         $mobileIdentifiers $this->em->getRepository(MobileIdentifiers::class)
  319.             ->findOneBy(['user' => $user'identifiant' => $data['identifiant']]);
  320.         if ($mobileIdentifiers) {
  321.             $this->em->remove($mobileIdentifiers);
  322.             $this->em->flush();
  323.             return new JsonResponse([
  324.                 'success' => true,
  325.                 'message' => 'Notification token deleted successfully'
  326.             ], 200);
  327.         }
  328.         return new JsonResponse([
  329.             'success' => false,
  330.             'message' => 'Token not found'
  331.         ], 404);
  332.     }
  333.     /**
  334.      * Mettre à jour le mot de passe.
  335.      * @param Request $request
  336.      * @return JsonResponse
  337.      */
  338.     public function updatePassword(Request $request): JsonResponse
  339.     {
  340.         if (!$request->isMethod('POST')) {
  341.             return new JsonResponse(['error' => 'Method not allowed'], 405);
  342.         }
  343.         $user $this->getUser();
  344.         if (!$user) {
  345.             return new JsonResponse(['error' => 'Unauthorized'], 401);
  346.         }
  347.         $data $request->toArray();
  348.         if($data['password'] !== $data['password_confirm']){
  349.             return new JsonResponse(['error' => 'Passwords do not match']);
  350.         }
  351.         $user->setPassword($this->passwordEncoder->encodePassword($user$data['password']));
  352.         $this->em->persist($user);
  353.         $this->em->flush();
  354.         return new JsonResponse(['success' => true]);
  355.     }
  356.     /**
  357.      * Avatar Base64.
  358.      * @param string $base64String
  359.      * @return array|string[]
  360.      */
  361.     private function detectImageTypeFromBase64(string $base64String): array
  362.     {
  363.         // Supprimer le préfixe data:image/...;base64, si présent
  364.         if (preg_match('/^data:image\/(\w+);base64,(.+)$/'$base64String$matches)) {
  365.             $extension $matches[1]; // jpeg, png, gif, etc.
  366.             $data $matches[2];
  367.             $mimeTypes = [
  368.                 'jpeg' => 'image/jpeg',
  369.                 'jpg' => 'image/jpeg',
  370.                 'png' => 'image/png',
  371.                 'gif' => 'image/gif',
  372.                 'webp' => 'image/webp',
  373.             ];
  374.             return [
  375.                 'extension' => $extension === 'jpg' 'jpeg' $extension,
  376.                 'mimeType' => $mimeTypes[$extension] ?? 'image/jpeg',
  377.                 'data' => $data
  378.             ];
  379.         }
  380.         // Si pas de préfixe, supposer que c'est du JPEG par défaut
  381.         return [
  382.             'extension' => 'jpeg',
  383.             'mimeType' => 'image/jpeg',
  384.             'data' => $base64String
  385.         ];
  386.     }
  387.     /**
  388.      * Supprimer un compte utilisateur.
  389.      * @param Request $request
  390.      * @return JsonResponse
  391.      */
  392.     public function deleteAccount(Request $request): JsonResponse
  393.     {
  394.         if (!$request->isMethod('POST')) {
  395.             return new JsonResponse(['error' => 'Method not allowed'], 405);
  396.         }
  397.         $user $this->getUser();
  398.         if (!$user) {
  399.             return new JsonResponse(['error' => 'Unauthorized'], 401);
  400.         }
  401.         $data $request->toArray();
  402.         if($data['mail'] != $user->getEmail()) {
  403.             return new JsonResponse(['success' => false]);
  404.         }
  405.         $candidate $user->getCandidate();
  406.         if($candidate != null) {
  407.             $groups $this->em->getRepository(Groups::class)->findBy(["userOne" => $user]);
  408.             if($groups !== null){
  409.                 foreach ($groups as $group) {
  410.                     $messages $this->em->getRepository(Messages::class)->findBy(['group' => $group->getId()]);
  411.                     foreach ($messages as $message) {
  412.                         $this->em->remove($message);
  413.                         $this->em->flush();
  414.                     }
  415.                     $this->em->remove($group);
  416.                 }
  417.             }
  418.             $groups $this->em->getRepository(Groups::class)->findBy(["userTwo" => $user]);
  419.             if($groups !== null){
  420.                 foreach ($groups as $group) {
  421.                     $messages $this->em->getRepository(Messages::class)->findBy(['group' => $group->getId()]);
  422.                     foreach ($messages as $message) {
  423.                         $this->em->remove($message);
  424.                         $this->em->flush();
  425.                     }
  426.                     $this->em->remove($group);
  427.                 }
  428.             }
  429.             $queries $this->em->getRepository(CandidatesHasStatistics::class)->findBy(["candidate" => $candidate]);
  430.             if($queries !== null){
  431.                 foreach ($queries as $query) {
  432.                     $this->em->remove($query);
  433.                 }
  434.             }
  435.             $queries $this->em->getRepository(CandidatesHasLikes::class)->findBy(["candidate" => $candidate]);
  436.             if($queries !== null){
  437.                 foreach ($queries as $query) {
  438.                     $this->em->remove($query);
  439.                 }
  440.             }
  441.             $queries $this->em->getRepository(CandidatesHasComments::class)->findBy(["candidate" => $candidate]);
  442.             if($queries !== null){
  443.                 foreach ($queries as $query) {
  444.                     $this->em->remove($query);
  445.                 }
  446.             }
  447.             $this->em->remove($candidate);
  448.         }
  449.         $this->em->remove($user);
  450.         $this->em->flush();
  451.         $delAccount = new AccountDeleted();
  452.         $delAccount->setEmail($data['mail']);
  453.         $delAccount->setMotif($data['motif']);
  454.         $delAccount->setCreatedAt(new \DateTime("now"));
  455.         $delAccount->setUpdatedAt(new \DateTime("now"));
  456.         $this->em->persist($delAccount);
  457.         $this->em->flush();
  458.         return new JsonResponse(['success' => true]);
  459.     }
  460. }