src/Controller/AdminController.php line 855

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use ZipArchive;
  4. use App\Entity\Logs;
  5. use App\Entity\Mail;
  6. use App\Entity\Step;
  7. use App\Entity\User;
  8. use App\Entity\Serie;
  9. use App\Form\UserType;
  10. use DateTimeInterface;
  11. use App\Entity\Answers;
  12. use App\Entity\Question;
  13. use App\Entity\Csvimport;
  14. use Doctrine\Persistence\ManagerRegistry;
  15. use Symfony\Component\HttpFoundation\Request;
  16. use Symfony\Component\HttpFoundation\Response;
  17. use Symfony\Component\Routing\Annotation\Route;
  18. use Symfony\Component\Validator\Constraints\Length;
  19. use Symfony\Component\Validator\Constraints\DateTime;
  20. use Symfony\Component\Validator\Constraints\NotBlank;
  21. use Symfony\Component\Form\Extension\Core\Type\TextType;
  22. use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
  23. use Symfony\Component\Form\Extension\Core\Type\NumberType;
  24. use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  25. use Symfony\Component\Form\Extension\Core\Type\PasswordType;
  26. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  27. use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
  28. // clef de chiffrement utilisée pour les réponse, elle est cachée là dedans
  29. define("ENCRYPTION_KEY""2d8e715d3bb3643ce56f5edc724dc1cfb60b226d85cc379b72f35c6d361d071bfa8b9a4f68810373d252cdf0892a959d371da6c230398780f87e74e27917ffce");
  30. class AdminController extends AbstractController
  31. {
  32.     // Sert uniquement à avoir une page admin de base
  33.     #[Route('/admin'name'admin')]
  34.     public function index(): Response
  35.     {
  36.         return $this->render('admin/index.html.twig', [
  37.             'controller_name' => 'AdminController',
  38.         ]);
  39.     }
  40.     // Cette fonction permet de créer des utilisateurs UN par UN
  41.     #[Route('/backoffice/user/create'name'user_create')]
  42.     public function createUser(Request $requestUserPasswordHasherInterface $userPasswordHasherManagerRegistry $manager): Response
  43.     {
  44.         // Impossible de faire ça sans être admin, même avec le chemin d'accès
  45.         $this->denyAccessUnlessGranted("ROLE_ADMIN"null"Access DENIED");
  46.         $entityManager $manager->getManager();
  47.         $user = new User();
  48.         $userForm $this->createForm(UserType::class, $user); //On utilise le formulaire UserType pour créer un utilisateur. (src/Form/UserForm.php)
  49.         $userForm->handleRequest($request);
  50.         // On vérifie que le formulaire est bien rempli avant d'enregistrer en base de donnée
  51.         if ($request->isMethod('post') && $userForm->isValid()) {
  52.             $data $userForm->getData();
  53.             switch ($data->getRoles()) {
  54.                 case "ROLE_ADMIN":
  55.                     $user->setRoles(['ROLE_ADMIN']);
  56.                     break;
  57.                 case "ROLE_USER":
  58.                     $user->setRoles(['ROLE_USER']);
  59.                     break;
  60.                 case ["ROLE_ADMIN""ROLE_USER"]:
  61.                     $user->setRoles(['ROLE_ADMIN''ROLE_USER']);
  62.                     break;
  63.             }
  64.             // ici on hash le mot de passe, c'est pour ça qu'on ne le voit pas en clair dans la base de donnée
  65.             $user->setPassword(
  66.                 $userPasswordHasher->hashPassword(
  67.                     $user,
  68.                     $userForm->get('password')->getData()
  69.                 )
  70.             );
  71.             $entityManager->persist($user);
  72.             $entityManager->flush();
  73.             return $this->redirect($this->generateUrl('user_backoffice'));
  74.         }
  75.         return $this->render('index/dataform.html.twig', [
  76.             'formName' => "Création d'utilisateur",
  77.             'dataForm' => $userForm->createView(),
  78.             "table" => "userCreate",
  79.         ]);
  80.     }
  81.     // Cette fonction sert à afficher le tableau des utilisateurs dans le tableau de bord des Admin
  82.     #[Route('/backoffice/user'name'user_backoffice')]
  83.     public function manageUser(ManagerRegistry $manager): Response
  84.     {
  85.         // Impossible de faire ça sans être admin, même avec le chemin d'accès
  86.         $this->denyAccessUnlessGranted("ROLE_ADMIN"null"Access DENIED");
  87.         $entityManager $manager->getManager();
  88.         $userRepository $entityManager->getRepository(User::class);
  89.         $logsRepository $entityManager->getRepository(Logs::class); // On se prépare aussi à chercher les logs afin de pouvoir afficher le temps passé par utilisateur sur le questionnaire
  90.         // On cherche tous les utilisateurs
  91.         $users $userRepository->findAll();
  92.         foreach ($users as $user) {
  93.             $logs $logsRepository->findBy(["user" => $user]);
  94.             $firstlog $logsRepository->findOneBy(["user" => $user"serieId" => 1]);
  95.             $tenthlog $logsRepository->findOneBy(["user" => $user"serieId" => 10]);
  96.             if (isset($tenthlog)) {
  97.                 $duree[$user->getId()] = date('H:i:s'$this->activeTime($logs$firstlog$tenthlog)[1] - 3600); // activeTime est une fonction créée plus bas dans le code pour calculer le temps passé devant le questionnaire sans compter les déconnexions
  98.             }
  99.         }
  100.         return $this->render('admin/backofficeUser.html.twig', [
  101.             'users' => $users,
  102.             'duree' => $duree,
  103.             'table' => "user",
  104.             'session' => $_SESSION,
  105.         ]);
  106.     }
  107.     // Cette fonction sert à afficher le tableau des mails dans le tableau de bord des Admin
  108.     #[Route('/backoffice/mail'name'mail_backoffice')]
  109.     public function manageMail(ManagerRegistry $manager): Response
  110.     {
  111.         // Impossible de faire ça sans être admin, même avec le chemin d'accès
  112.         $this->denyAccessUnlessGranted("ROLE_ADMIN"null"Access DENIED");
  113.         $entityManager $manager->getManager();
  114.         $mailRepository $entityManager->getRepository(Mail::class);
  115.         $mails $mailRepository->findAll();
  116.         return $this->render('admin/backofficeEmail.html.twig', [
  117.             'mails' => $mails,
  118.             'table' => "mail",
  119.         ]);
  120.     }
  121.     // Cette fonction permet de créer des utilisateurs UN par UN
  122.     #[Route('/backoffice/mail/create'name'mail_create')]
  123.     public function createmail(Request $requestManagerRegistry $manager): Response
  124.     {
  125.         // Impossible de faire ça sans être admin, même avec le chemin d'accès
  126.         $this->denyAccessUnlessGranted("ROLE_ADMIN"null"Access DENIED");
  127.         $entityManager $manager->getManager();
  128.         $mail = new Mail();
  129.         $mailForm $this->createFormBuilder()
  130.             ->add('code'TextType::class, [
  131.                 'label' => "Code",
  132.                 'required' => true,
  133.                 "attr" => [
  134.                     'class' => "form-control mb-3"
  135.                 ]
  136.             ])
  137.             ->add('adresses'TextType::class, [
  138.                 'label' => 'Adresse',
  139.                 'required' => true,
  140.                 "attr" => [
  141.                     "class" => "form-control mb-3"
  142.                 ]
  143.             ])
  144.             ->add('valider'SubmitType::class, [
  145.                 'label' => 'Valider',
  146.                 "attr" => [
  147.                     "class" => "col-6 form-control btn btn-success mx-auto mt-5",
  148.                     "type" => "submit"
  149.                 ]
  150.             ])
  151.             ->getForm();
  152.         //On applique la Requête à notre formulaire
  153.         $mailForm->handleRequest($request);
  154.         // On vérifie que le formulaire est bien rempli avant d'enregistrer en base de donnée
  155.         if ($request->isMethod('post') && $mailForm->isValid()) {
  156.             $mail->setCode($mailForm->get('code')->getData());
  157.             $mail->setAdresses($mailForm->get('adresses')->getData());
  158.             $entityManager->persist($mail);
  159.             $entityManager->flush();
  160.             return $this->redirect($this->generateUrl('mail_backoffice'));
  161.         }
  162.         return $this->render('index/dataform.html.twig', [
  163.             'formName' => "Création d'utilisateur",
  164.             'dataForm' => $mailForm->createView(),
  165.             "table" => "mailCreate",
  166.         ]);
  167.     }
  168.     // Cette fonction permet de mettre à jour les données des étapes du questionnaire (consignes et rappel de consignes)
  169.     #[Route('/backoffice/mail/update-{mailId}'name'mail_update')]
  170.     public function updateMail(ManagerRegistry $managerRequest $requestint $mailId 0): Response
  171.     {
  172.         // Impossible de faire ça sans être admin, même avec le chemin d'accès
  173.         $this->denyAccessUnlessGranted("ROLE_ADMIN"null"Access DENIED");
  174.         //Cette méthode a pour objectif de sélectionner une étape déjà existant et de modifier son contenu à l'aide d'un formulaire présenté à l'étape
  175.         //Afin de pouvoir modifier une étape déjà présent dans notre base de données, nous devons faire appel à l'Entity Manager pour dialoguer avec notre BDD, ainsi qu'au Repository de l'étape pour récupérer l'ee 'étape pertinent
  176.         $entityManager $manager->getManager();
  177.         $etapeRepository $entityManager->getRepository(Mail::class);
  178.         $mail $etapeRepository->find($mailId);
  179.         if (!$mail) {
  180.             return $this->redirectToRoute('mail_backoffice');
  181.         }
  182.         //Si le mail existe, nous créons un formulaire auquel nous lions le mail récupéré
  183.         $mailForm $this->createFormBuilder($mail)
  184.             ->add('code'TextType::class, [
  185.                 'label' => "Code",
  186.                 'required' => true,
  187.                 "attr" => [
  188.                     'class' => "form-control mb-3"
  189.                 ]
  190.             ])
  191.             ->add('adresses'TextType::class, [
  192.                 'label' => 'Adresse',
  193.                 'required' => true,
  194.                 "attr" => [
  195.                     "class" => "form-control mb-3"
  196.                 ]
  197.             ])
  198.             ->add('valider'SubmitType::class, [
  199.                 'label' => 'Valider',
  200.                 "attr" => [
  201.                     "class" => "col-6 form-control btn btn-success mx-auto mt-5",
  202.                     "type" => "submit"
  203.                 ]
  204.             ])
  205.             ->getForm();
  206.         //On applique la Requête à notre formulaire
  207.         $mailForm->handleRequest($request);
  208.         // On enregistre en base de donnée uniquement si le formulaire est bien rempli
  209.         if ($request->isMethod('post') && $mailForm->isValid()) {
  210.             $entityManager->persist($mail);
  211.             $entityManager->flush();
  212.             return $this->redirectToRoute('mail_backoffice');
  213.         }
  214.         //Si le user n'est pas validé, nous envoyons l'Utilisateur vers la page du formulaire
  215.         return $this->render('index/dataform.html.twig', [
  216.             'formName' => 'Modification du mail',
  217.             'dataForm' => $mailForm->createView(),
  218.             'lang' => 'fra',
  219.             'table' => 'user',
  220.         ]);
  221.     }
  222.     // Cette fonction sert à afficher le tableau des série dans le tableau de bord des Admin
  223.     #[Route('/backoffice/serie'name'serie_backoffice')]
  224.     public function manageSerie(ManagerRegistry $manager): Response
  225.     {
  226.         // Impossible de faire ça sans être admin, même avec le chemin d'accès
  227.         $this->denyAccessUnlessGranted("ROLE_ADMIN"null"Access DENIED");
  228.         $entityManager $manager->getManager();
  229.         $serieRepository $entityManager->getRepository(Serie::class);
  230.         $series $serieRepository->findAll();
  231.         return $this->render('admin/backofficeSerie.html.twig', [
  232.             'series' => $series,
  233.             'table' => "serie"
  234.         ]);
  235.     }
  236.     // Cette fonction sert à afficher le tableau des étapes dans le tableau de bord des Admin
  237.     #[Route('/backoffice/step'name'step_backoffice')]
  238.     public function manageStep(ManagerRegistry $manager): Response
  239.     {
  240.         // Impossible de faire ça sans être admin, même avec le chemin d'accès
  241.         $this->denyAccessUnlessGranted("ROLE_ADMIN"null"Access DENIED");
  242.         $entityManager $manager->getManager();
  243.         $stepRepository $entityManager->getRepository(Step::class);
  244.         $steps $stepRepository->findAll();
  245.         return $this->render('admin/backofficeStep.html.twig', [
  246.             'steps' => $steps,
  247.             'table' => "step"
  248.         ]);
  249.     }
  250.     // Cette fonction sert à afficher le tableau des Questions et Réponses dans le tableau de bord des Admin
  251.     #[Route('/backoffice/question'name'question_backoffice')]
  252.     public function manageQuestion(ManagerRegistry $manager): Response
  253.     {
  254.         // Impossible de faire ça sans être admin, même avec le chemin d'accès
  255.         $this->denyAccessUnlessGranted("ROLE_ADMIN"null"Access DENIED");
  256.         $entityManager $manager->getManager();
  257.         $questionRepository $entityManager->getRepository(Question::class);
  258.         $questions $questionRepository->findAll();
  259.         return $this->render('admin/backofficeQuestion.html.twig', [
  260.             'questions' => $questions,
  261.             'table' => "question"
  262.         ]);
  263.     }
  264.     // function ExportCSV(){
  265.     //     $this->load->dbutil();
  266.     //     $this->load->helper('file');
  267.     //     $this->load->helper('download');
  268.     //     $delimiter = ";";
  269.     //     $newline = "\r\n";
  270.     //     $filename = "mpfusers_".date('Y-m-d').".csv";
  271.     //     $query = "SELECT first_name as 'Nom', username as Login, sexe as 'Sexe', age as 'Age', serieEnCours as 'Serie', FROM_UNIXTIME(last_login) as 'Dernière connexion' , FROM_UNIXTIME(created_on) as 'Création' FROM  users LEFT JOIN users_groups ON users.id = users_groups.user_id   WHERE users_groups.group_id = 2 order by users.id desc";
  272.     //     $result = $this->db->query($query);
  273.     //     $data = $this->dbutil->csv_from_result($result, $delimiter, $newline);
  274.     //     force_download($filename, $data);
  275.     //     }
  276.     // Cette fonction sert à afficher les logs d'un utilisateur dans le tableau de bord des Admin
  277.     #[Route('/backoffice/logs/user-{userId}'name'user_logs')]
  278.     public function userLogs(ManagerRegistry $manager$userId 1): Response
  279.     {
  280.         // Impossible de faire ça sans être admin, même avec le chemin d'accès
  281.         $this->denyAccessUnlessGranted("ROLE_ADMIN"null"Access DENIED");
  282.         $entityManager $manager->getManager();
  283.         $userRepository $entityManager->getRepository(User::class);
  284.         $logsRepository $entityManager->getRepository(Logs::class);
  285.         $user $userRepository->find($userId);
  286.         $logs $logsRepository->findBy(["user" => $user]);
  287.         $firstlog $logsRepository->findOneBy(["user" => $user"serieId" => 1]);
  288.         $tenthlog $logsRepository->findOneBy(["user" => $user"serieId" => 10]);
  289.         if (!$logs) {
  290.             return $this->render("admin/backofficeUserLogs.html.twig", [
  291.                 "user" => $user,
  292.                 "logs" => "vide",
  293.                 "table" => "user"
  294.             ]);
  295.         }
  296.         // Tout se passe ici, cette méthode est créée plus bas
  297.         $resultat $this->activeTime($logs$firstlog$tenthlog);
  298.         return $this->render("admin/backofficeUserLogs.html.twig", [
  299.             "user" => $user,
  300.             "logs" => $logs,
  301.             "nlogs" => count($logs),
  302.             "duree" => $resultat[0],
  303.             "active" => $resultat[4],
  304.             "totalActiveTime" => date('H:i:s'$resultat[1] - 3600),
  305.             "totalTime" => $resultat[3],
  306.             "arrayIP" => $resultat[2],
  307.             "table" => "user"
  308.         ]);
  309.     }
  310.     // Cette fonction sert à mettre à jour un utilisateur dans la base de donnée
  311.     #[Route('/backoffice/user/update-{userId}'name'user_update')]
  312.     public function updateUser(ManagerRegistry $managerRequest $requestUserPasswordHasherInterface $userPasswordHasherint $userId 0): Response
  313.     {
  314.         // Impossible de faire ça sans être admin, même avec le chemin d'accès
  315.         $this->denyAccessUnlessGranted("ROLE_ADMIN"null"Access DENIED");
  316.         //Cette méthode a pour objectif de sélectionner un Utilisateur déjà existant et de modifier son contenu à l'aide d'un formulaire présenté à l'Utilisateur
  317.         //Afin de pouvoir modifier un Utilisateur déjà présent dans notre base de données, nous devons faire appel à l'Entity Manager pour dialoguer avec notre BDD, ainsi qu'au Repository de Utilisateur pour récupérer le Utilisateur pertinent
  318.         $entityManager $manager->getManager();
  319.         $userRepository $entityManager->getRepository(User::class);
  320.         $etapeRepository $entityManager->getRepository(Step::class);
  321.         $serieRepository $entityManager->getRepository(Serie::class);
  322.         //Nous récupérons le user dont l'ID est renseigné
  323.         $user $userRepository->find($userId);
  324.         //Si aucune user n'est trouvé, $user vaut null et nous retournons à l'index
  325.         //En mettant à la fonction, on redirige l'Utilisateur et on évite les erreurs
  326.         if (!$user) {
  327.             return $this->redirectToRoute('user_backoffice');
  328.         }
  329.         //Si le user existe, nous créons un formulaire auquel nous lions le user récupéré
  330.         $userForm $this->createFormBuilder($user)
  331.             ->add('username'TextType::class, [
  332.                 'label' => "Login",
  333.                 'required' => false,
  334.                 "attr" => [
  335.                     'class' => "form-control mb-3"
  336.                 ]
  337.             ])
  338.             ->add('name'TextType::class, [
  339.                 'label' => 'Nom',
  340.                 'required' => false,
  341.                 "attr" => [
  342.                     "class" => "form-control mb-3"
  343.                 ]
  344.             ])
  345.             ->add('roles'ChoiceType::class, [
  346.                 'label' => "Role",
  347.                 'required' => false,
  348.                 "choices" => [
  349.                     'Utilisateur.........................................................................................' => 'ROLE_USER',
  350.                     "Admin" => 'ROLE_ADMIN'
  351.                 ],
  352.                 'expanded' => true,
  353.                 'multiple' => true,
  354.                 "attr" => [
  355.                     'class' => "justify-content-evenly mb-3"
  356.                 ]
  357.             ])
  358.             ->add('password'PasswordType::class, [
  359.                 // instead of being set onto the object directly,
  360.                 // this is read and encoded in the controller
  361.                 'label' => "Nouveau mot de passe",
  362.                 'mapped' => false,
  363.                 'required' => false,
  364.                 "attr" => [
  365.                     'class' => "form-control mb-3",
  366.                     'autocomplete' => 'new-password'
  367.                 ],
  368.                 'constraints' => [
  369.                     new Length([
  370.                         'min' => 4,
  371.                         'minMessage' => 'Your password should be at least {{ limit }} characters',
  372.                         // max length allowed by Symfony for security reasons
  373.                         'max' => 4096,
  374.                     ]),
  375.                 ]
  376.             ])
  377.             ->add('currentSerie'NumberType::class, [
  378.                 'label' => 'Serie en cours',
  379.                 'required' => false,
  380.                 "attr" => [
  381.                     "class" => "form-control mb-3"
  382.                 ]
  383.             ])
  384.             ->add('infos'TextType::class, [
  385.                 'label' => 'infos',
  386.                 'required' => false,
  387.                 "attr" => [
  388.                     "class" => "form-control mb-3"
  389.                 ]
  390.             ])
  391.             ->add('sexe'TextType::class, [
  392.                 'label' => 'sexe',
  393.                 'required' => false,
  394.                 "attr" => [
  395.                     "class" => "form-control mb-3"
  396.                 ]
  397.             ])
  398.             ->add('age'NumberType::class, [
  399.                 'label' => 'age',
  400.                 'required' => false,
  401.                 "attr" => [
  402.                     "class" => "form-control mb-3"
  403.                 ]
  404.             ])
  405.             ->add('valider'SubmitType::class, [
  406.                 'label' => 'Valider',
  407.                 "attr" => [
  408.                     "class" => "col-6 form-control btn btn-success mx-auto mt-5",
  409.                     "type" => "submit"
  410.                 ]
  411.             ])
  412.             ->getForm();
  413.         //On applique la Requête à notre formulaire
  414.         $userForm->handleRequest($request);
  415.         //Si notre Formulaire de user est rempli ET validé via une Request POST, nous l'envoyons vers notre base de données, ce qui équivaut à la modification du user récupéré précédemment
  416.         if ($request->isMethod('post') && $userForm->isValid()) {
  417.             //Nous vérifions l'absence de user au même nom dans notre BDD
  418.             $userDuplicata $userRepository->findOneBy(['name' => $user->getName()]);
  419.             if ($userDuplicata && ($userDuplicata->getId() != $user->getId())) { //Si un doublon (qui n'est pas notre produit déjà présent) est trouvé
  420.                 //Nous signifions qu'une entrée identique existe
  421.                 ////////////////////////// Message d'erreur à remplir
  422.             } else {
  423.                 if ($userForm->get('password')->getData() != null) {
  424.                     $user->setPassword(
  425.                         $userPasswordHasher->hashPassword($user$userForm->get('password')->getData())
  426.                     );
  427.                 }
  428.                 $user->setCurrentStep($etapeRepository->findOneBy([
  429.                     "serie" => $serieRepository->find($user->getCurrentSerie()),
  430.                     "number" => 1
  431.                 ])->getId());
  432.                 $entityManager->persist($user);
  433.                 $entityManager->flush();
  434.             }
  435.             return $this->redirectToRoute('user_backoffice');
  436.         }
  437.         //Si le user n'est pas validé, nous envoyons l'Utilisateur vers la page du formulaire
  438.         return $this->render('index/dataform.html.twig', [
  439.             'formName' => 'Modification du user',
  440.             'dataForm' => $userForm->createView(),
  441.             'lang' => 'fra',
  442.             'table' => 'user',
  443.         ]);
  444.     }
  445.     // Cette fonction permet de cacher ou non les utilisateurs dans le tableau de bord des Admins
  446.     #[Route('/backoffice/user/hideUser'name'user_hide')]
  447.     public function hideUser(ManagerRegistry $manager): Response
  448.     {
  449.         // Impossible de faire ça sans être admin, même avec le chemin d'accès
  450.         $this->denyAccessUnlessGranted("ROLE_ADMIN"null"Access DENIED");
  451.         $entityManager $manager->getManager();
  452.         $userRepository $entityManager->getRepository(User::class);
  453.         $cookie explode("|"$_COOKIE['hiddenUsers']);
  454.         foreach ($cookie as $key => $value) {
  455.             $temp explode("*"$value);
  456.             $arrayCookie[$temp[0]] = $temp[1];
  457.         }
  458.         foreach ($arrayCookie as $id => $hide) {
  459.             $user $userRepository->find($id);
  460.             if ($user) {
  461.                 $user->setHidden($hide);
  462.                 $entityManager->persist($user);
  463.             }
  464.         }
  465.         $entityManager->flush();
  466.         return $this->redirectToRoute('user_backoffice');
  467.     }
  468.     // Cette fonction permet de mettre à jour les données des étapes du questionnaire (consignes et rappel de consignes)
  469.     #[Route('/backoffice/step/update-{stepId}'name'step_update')]
  470.     public function updateStep(ManagerRegistry $managerint $stepId 0): Response
  471.     {
  472.         // Impossible de faire ça sans être admin, même avec le chemin d'accès
  473.         $this->denyAccessUnlessGranted("ROLE_ADMIN"null"Access DENIED");
  474.         //Cette méthode a pour objectif de sélectionner une étape déjà existant et de modifier son contenu à l'aide d'un formulaire présenté à l'étape
  475.         //Afin de pouvoir modifier une étape déjà présent dans notre base de données, nous devons faire appel à l'Entity Manager pour dialoguer avec notre BDD, ainsi qu'au Repository de l'étape pour récupérer l'ee 'étape pertinent
  476.         $entityManager $manager->getManager();
  477.         $etapeRepository $entityManager->getRepository(Step::class);
  478.         $serieRepository $entityManager->getRepository(Serie::class);
  479.         $step $etapeRepository->find($stepId);
  480.         $serie $serieRepository->find($step->getSerie()->getId());
  481.         // On enregistre en base de donnée uniquement si le formulaire est bien rempli
  482.         if (isset($_POST) && isset($_POST["valider"])) {
  483.             $step->setDescription($_POST["content"]);
  484.             isset($_POST["step-progress"]) ? $step->setProgress($_POST["step-progress"]) : null;
  485.             isset($_POST["step-number"]) ? $step->setNumber($_POST["step-number"]) : null;
  486.             $entityManager->persist($step);
  487.             $entityManager->flush();
  488.             return $this->redirectToRoute('step_backoffice');
  489.         }
  490.         return $this->render('admin/backofficeEditor.html.twig', [
  491.             'serie' => $serie,
  492.             'target' => 'step',
  493.             'step' => $step,
  494.             'content' => $step->getDescription(),
  495.             'previousPage' => "/backoffice/step/update-" $stepId,
  496.             'table' => "etape",
  497.         ]);
  498.     }
  499.     // Cette fonction permet de mettre à jour les questions et les réponses du questionnaire
  500.     #[Route('/backoffice/question/update-{questionId}'name'question_update')]
  501.     public function updateQuestion(ManagerRegistry $managerint $questionId 0): Response
  502.     {
  503.         // Impossible de faire ça sans être admin, même avec le chemin d'accès
  504.         $this->denyAccessUnlessGranted("ROLE_ADMIN"null"Access DENIED");
  505.         $entityManager $manager->getManager();
  506.         $questionRepository $entityManager->getRepository(Question::class);
  507.         $serieRepository $entityManager->getRepository(Serie::class);
  508.         $question $questionRepository->find($questionId);
  509.         $serie $serieRepository->find($question->getSerie()->getId());
  510.         // On enregistre en base de donnée uniquement si le formulaire est bien rempli
  511.         if (isset($_POST) && isset($_POST["valider"])) {
  512.             $question->setContent($_POST["question-content"]);
  513.             isset($_POST["question-content"]) ? $question->setContent($_POST["question-content"]) : null;
  514.             isset($_POST["question-serie"]) ? $question->setSerie($serieRepository->find($_POST["question-serie"])) : null;
  515.             isset($_POST["question-type"]) ? $question->setType($_POST["question-type"]) : null;
  516.             foreach ($question->getChoices() as $choice) {
  517.                 isset($_POST["choice-number-" $choice->getNumber()]) ? $choice->setNumber($_POST["choice-number-" $choice->getNumber()]) : null;
  518.                 isset($_POST["choice-content-" $choice->getNumber()]) ? $choice->setNumber($_POST["choice-content-" $choice->getNumber()]) : null;
  519.                 $entityManager->persist($choice);
  520.             }
  521.             $entityManager->persist($question);
  522.             $entityManager->flush();
  523.             return $this->redirectToRoute('question_backoffice');
  524.         }
  525.         return $this->render('admin/backofficeEditor.html.twig', [
  526.             'serie' => $serie,
  527.             'target' => 'question',
  528.             'question' => $question,
  529.             'choices' => $question->getChoices(),
  530.             'content' => $question->getContent(),
  531.             'previousPage' => "/backoffice/question/update-" $questionId,
  532.             'table' => "etape",
  533.         ]);
  534.     }
  535.     ////////////////////////////////////////////////////////////////////////////////////////
  536.     //////////////////////////////  Cryptage  //////////////////////////////////////////////
  537.     ////////////////////////////////////////////////////////////////////////////////////////
  538.     // Cette méthode est là pour le chiffrement des réponses
  539.     private function jencrypt($text$key)
  540.     {
  541.         // $text = serialize($text);
  542.         $key2 sodium_hex2bin(substr($key064)); // On prend les 64 premiers caractères de la clef en haut de page car seuls eux sont les bons puis on les convertit du format hexadecimal en binaire afin de pouvoir utiliser la clef dans la fonction
  543.         $encrypt sodium_crypto_box_seal($text$key2); // Ici on chiffre les données (qui seront les résultats des réponses sous forme de texte) avec la clef que nous venons de convertir.
  544.         $encoded base64_encode($encrypt); // 2eme chiffrement avec une méthode dite "de base 64" pour plus de sécurité
  545.         return $encoded;
  546.     }
  547.     // Cette fonction sert à effectuer l'action de chiffrement des réponses (on fait appel à la méthode jencrypt) et à l'enregistrement en base de donnée
  548.     #[Route('/backoffice/reponsetxt/{userId}'name'reponse')]
  549.     public function getAnswersTxt(ManagerRegistry $manager$userId): Response
  550.     {
  551.         $entityManager $manager->getManager();
  552.         $answersRepository $entityManager->getRepository(Answers::class);
  553.         $userRepository $entityManager->getRepository(User::class);
  554.         $user $userRepository->findOneBy(["id" => $userId]); // On selectionne l'utilisateur sur lequel on a cliqué
  555.         $reponses $answersRepository->findBy(["user" => $user], ["serie" => "ASC"]); // On cherche toutes les réponses de l'utilisateur et on les ordonnes par ordre croissant de série
  556.         $corps ""//Notre texte chiffré, que l'on va ajouter petit à petit
  557.         $corps .= $this->jencrypt("# **************************** \r\n"ENCRYPTION_KEY) . "\r\n"// Ajout d'une ligne de séparation chiffrée
  558.         $corps .= $this->jencrypt("# Rep 2022 de : " $user->getUsername() . "|" $user->getSexe() . $user->getAge() . "|" $user->getInfos(), ENCRYPTION_KEY) . "\r\n"// Ajout des infos de l'utilisateur en en-tête de réponse
  559.         foreach ($reponses as $row) {
  560.             // Ajout de chaque série de réponse
  561.             $corps .= $this->jencrypt("\r\n# **************************** \r\n"ENCRYPTION_KEY) . "\r\n";
  562.             $corps .= $this->jencrypt("# serie " $row->getSerie()->getId() . " \r\n"ENCRYPTION_KEY) . "\r\n";
  563.             $corps .= $this->jencrypt("# **************************** \r\n"ENCRYPTION_KEY) . "\r\n";
  564.             $corps .= $row->getFlow();
  565.         }
  566.         // Ici on déclenche le téléchargement du fichier texte qui vient d'être créé avec les réponses
  567.         $this->force_download($user->getUsername() . '-crypte.txt'$corps);
  568.         // On revient au tableau des utilisateurs
  569.         return $this->redirectToRoute('user_backoffice');
  570.     }
  571.     // Cette fonction sert à effectuer l'action de chiffrement des réponses (on fait appel à la méthode jencrypt) et à l'enregistrement en base de donnée
  572.     #[Route('/backoffice/download'name'user_dl')]
  573.     public function downloadTxt(ManagerRegistry $manager): Response
  574.     {
  575.         // Impossible de faire ça sans être admin, même avec le chemin d'accès
  576.         $this->denyAccessUnlessGranted("ROLE_ADMIN"null"Access DENIED");
  577.         ob_start();
  578.         $entityManager $manager->getManager();
  579.         $answersRepository $entityManager->getRepository(Answers::class);
  580.         $userRepository $entityManager->getRepository(User::class);
  581.         $cookie explode("|"$_COOKIE['toDownload']);
  582.         
  583.         $arrayFiles = [];
  584.         # create new zip opbject
  585.         $zip = new ZipArchive();
  586.         
  587.         # create a temp file & open it
  588.         $tmp_zip tempnam(".",'');
  589.         $zip->open($tmp_zipZipArchive::CREATE);
  590.         foreach ($cookie as $userId) {
  591.             $user $userRepository->find($userId);
  592.             
  593.             if ($user) {
  594.                 
  595.                 $answers $answersRepository->findBy(["user" => $user], ["serie" => "ASC"]); // On cherche toutes les réponses de l'utilisateur et on les ordonnes par ordre croissant de série
  596.                 $corps ""//Notre texte chiffré, que l'on va ajouter petit à petit
  597.                 
  598.                 $corps .= $this->jencrypt("# **************************** \r\n"ENCRYPTION_KEY) . "\r\n"// Ajout d'une ligne de séparation chiffrée
  599.                 $corps .= $this->jencrypt("# Rep 2022 de : " $user->getUsername() . "|" $user->getSexe() . $user->getAge() . "|" $user->getInfos(), ENCRYPTION_KEY) . "\r\n"// Ajout des infos de l'utilisateur en en-tête de réponse
  600.                 
  601.                 foreach ($answers as $row) {
  602.                     // Ajout de chaque série de réponse
  603.                     $corps .= $this->jencrypt("\r\n# **************************** \r\n"ENCRYPTION_KEY) . "\r\n";
  604.                     $corps .= $this->jencrypt("# serie " $row->getSerie()->getId() . " \r\n"ENCRYPTION_KEY) . "\r\n";
  605.                     $corps .= $this->jencrypt("# **************************** \r\n"ENCRYPTION_KEY) . "\r\n";
  606.                     $corps .= $row->getFlow();
  607.                 }
  608.                 $corps trim($corps);
  609.                 
  610.                 $userName $user->getUsername();
  611.                 // $tmp_file = tempnam('.', $userName . '-crypte.txt');
  612.                 array_push($arrayFiles$userName '-crypte.txt');
  613.                 file_put_contents($userName '-crypte.txt'$corps);
  614.                 $zip->addFile($userName '-crypte.txt');
  615.                 
  616.                 $user->setDownloaded(1);
  617.                 $entityManager->persist($user);
  618.                 $entityManager->flush();
  619.             }
  620.         }
  621.         $zipsize $zip->statName("size");
  622.         $zip->close();
  623.         ob_end_flush();
  624.         
  625.         foreach ($arrayFiles as $file) {
  626.             unlink($file);
  627.         }
  628.         $this->force_download_2(date("Ymj_Hi") . '_lots_myBim-crypte.zip'file_get_contents($tmp_zip), $tmp_zip);
  629.         
  630.         // On revient au tableau des utilisateurs
  631.         return $this->redirectToRoute('user_backoffice');
  632.     }
  633.     /**
  634.      * Force Download
  635.      *
  636.      * Generates headers that force a download to happen
  637.      *
  638.      * @param    string    filename
  639.      * @param    mixed    the data to be downloaded
  640.      * @param    bool    whether to try and send the actual file MIME type
  641.      * @return    void
  642.      */
  643.     public function force_download($filename ''$data ''$set_mime FALSE)
  644.     {
  645.         if ($filename === '' or $data === '') {
  646.             return;
  647.         } elseif ($data === NULL) {
  648.             if (!@is_file($filename) or ($filesize = @filesize($filename)) === FALSE) {
  649.                 return;
  650.             }
  651.             $filepath $filename;
  652.             $filename explode('/'str_replace(DIRECTORY_SEPARATOR'/'$filename));
  653.             $filename end($filename);
  654.         } else {
  655.             // $filesize = strlen($data);
  656.         }
  657.         // Set the default MIME type to send
  658.         $mime 'application/octet-stream';
  659.         $x explode('.'$filename);
  660.         $extension end($x);
  661.         if ($set_mime === TRUE) {
  662.             if (count($x) === or $extension === '') {
  663.                 /* If we're going to detect the MIME type,
  664.                  * we'll need a file extension.
  665.                  */
  666.                 return;
  667.             }
  668.             // Load the mime types
  669.             $mimes = &$this->get_mimes();
  670.             // Only change the default MIME if we can find one
  671.             if (isset($mimes[$extension])) {
  672.                 $mime is_array($mimes[$extension]) ? $mimes[$extension][0] : $mimes[$extension];
  673.             }
  674.         }
  675.         /* It was reported that browsers on Android 2.1 (and possibly older as well)
  676.          * need to have the filename extension upper-cased in order to be able to
  677.          * download it.
  678.          *
  679.          * Reference: http://digiblog.de/2011/04/19/android-and-the-download-file-headers/
  680.          */
  681.         if (count($x) !== && isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/Android\s(1|2\.[01])/'$_SERVER['HTTP_USER_AGENT'])) {
  682.             $x[count($x) - 1] = strtoupper($extension);
  683.             $filename implode('.'$x);
  684.         }
  685.         if ($data === NULL && ($fp = @fopen($filepath'rb')) === FALSE) {
  686.             return;
  687.         }
  688.         // Clean output buffer
  689.         if (ob_get_level() !== && @ob_end_clean() === FALSE) {
  690.             @ob_clean();
  691.         }
  692.         // Generate the server headers
  693.         header('Content-Type: ' $mime);
  694.         header('Content-Disposition: attachment; filename="' $filename '"');
  695.         header('Expires: 0');
  696.         header('Content-Transfer-Encoding: binary');
  697.         // header('Content-Length: ' . $filesize);
  698.         header('Cache-Control: private, no-transform, no-store, must-revalidate');
  699.         // If we have raw data - just dump it
  700.         if ($data !== NULL) {
  701.             exit($data);
  702.         }
  703.         // Flush 1MB chunks of data
  704.         while (!feof($fp) && ($data fread($fp1048576)) !== FALSE) {
  705.             echo $data;
  706.         }
  707.         fclose($fp);
  708.         exit;
  709.     }
  710.     
  711.     public function force_download_2($filename ''$data ''$filePlace$set_mime FALSE)
  712.     {
  713.         if ($filename === '' or $data === '') {
  714.             return;
  715.         } elseif ($data === NULL) {
  716.             if (!@is_file($filename) or ($filesize = @filesize($filename)) === FALSE) {
  717.                 return;
  718.             }
  719.             $filepath $filename;
  720.             $filename explode('/'str_replace(DIRECTORY_SEPARATOR'/'$filename));
  721.             $filename end($filename);
  722.         } else {
  723.             // $filesize = strlen($data);
  724.         }
  725.         // Set the default MIME type to send
  726.         $mime 'application/octet-stream';
  727.         $x explode('.'$filename);
  728.         $extension end($x);
  729.         if ($set_mime === TRUE) {
  730.             if (count($x) === or $extension === '') {
  731.                 /* If we're going to detect the MIME type,
  732.                  * we'll need a file extension.
  733.                  */
  734.                 return;
  735.             }
  736.             // Load the mime types
  737.             $mimes = &$this->get_mimes();
  738.             // Only change the default MIME if we can find one
  739.             if (isset($mimes[$extension])) {
  740.                 $mime is_array($mimes[$extension]) ? $mimes[$extension][0] : $mimes[$extension];
  741.             }
  742.         }
  743.         /* It was reported that browsers on Android 2.1 (and possibly older as well)
  744.          * need to have the filename extension upper-cased in order to be able to
  745.          * download it.
  746.          *
  747.          * Reference: http://digiblog.de/2011/04/19/android-and-the-download-file-headers/
  748.          */
  749.         if (count($x) !== && isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/Android\s(1|2\.[01])/'$_SERVER['HTTP_USER_AGENT'])) {
  750.             $x[count($x) - 1] = strtoupper($extension);
  751.             $filename implode('.'$x);
  752.         }
  753.         if ($data === NULL && ($fp = @fopen($filepath'rb')) === FALSE) {
  754.             return;
  755.         }
  756.         // Clean output buffer
  757.         if (ob_get_level() !== && @ob_end_clean() === FALSE) {
  758.             @ob_clean();
  759.         }
  760.         // Generate the server headers
  761.         // header('Content-Type: octet-stream');
  762.         header('Content-Disposition: attachment; filename="' $filename '"');
  763.         header('Expires: 0');
  764.         header('Content-Transfer-Encoding: binary');
  765.         // header('Content-Length: ' . $filesize);
  766.         header('Cache-Control: private, no-transform, no-store, must-revalidate');
  767.         unlink($filePlace);
  768.         // If we have raw data - just dump it
  769.         if ($data !== NULL) {
  770.             exit($data);
  771.         }
  772.         // Flush 1MB chunks of data
  773.         while (!feof($fp) && ($data fread($fp1048576)) !== FALSE) {
  774.             echo $data;
  775.         }
  776.         fclose($fp);
  777.         // exit;
  778.     }
  779.     /**
  780.      * Returns the MIME types array from config/mimes.php
  781.      *
  782.      * @return    array
  783.      */
  784.     public function &get_mimes()
  785.     {
  786.         static $_mimes;
  787.         if (empty($_mimes)) {
  788.             $_mimes = array();
  789.         }
  790.         return $_mimes;
  791.     }
  792.     ///////////////////////// Import CSV //////////////////////////
  793.     // Cette fonction permet d'importer les fichiers CSV et ajouter des utilisateurs à la base de données
  794.     #[Route('/backoffice/importCSV'name'importCSV')]
  795.     function import(UserPasswordHasherInterface $userPasswordHasherManagerRegistry $manager): Response
  796.     {
  797.         if ($_FILES) {
  798.             if ($_FILES['file']['name'] != "") {
  799.                 $entityManager $manager->getManager();
  800.                 $fileName $_FILES['file']['tmp_name'];
  801.                 $fileHandle fopen($fileName"r") or exit("Impossible d'ouvrir le fichier !");
  802.                 //On boucle sur toutes les lignes du document CSV.
  803.                 while (($row fgetcsv($fileHandle0";")) !== FALSE) {
  804.                     // On rajoute une condition pour ne pas mettre la ligne des en-têtes des colonnes dans la base de données - comme un utilisateur s'appellant login - .
  805.                     if ($row[0] != "login") {
  806.                         $user = new User;
  807.                         $user->setUsername($row[0]);
  808.                         $user->setPassword(
  809.                             $userPasswordHasher->hashPassword($user$row[1])
  810.                         );
  811.                         $user->setName($row[2]);
  812.                         $entityManager->persist($user);
  813.                     }
  814.                 }
  815.                 $entityManager->flush();
  816.             }
  817.         }
  818.         return $this->redirectToRoute('user_backoffice');
  819.     }
  820.     ////////////////////////////////////////////////////////////////////////
  821.     ////////////////////////////////////////////////////////////////////////
  822.     // Ici on a une méthode qui permet de mesurer le temps passé devant le test moins celui passé déconnecté, et ce quel que soit le nombre de déconnexions
  823.     public function activeTime($logs$firstlog$tenthlog)
  824.     {
  825.         // Initialisation des variables
  826.         $i 1;
  827.         $totalActiveTime 0;
  828.         $totalTime 0;
  829.         $totalFormat 0;
  830.         $arrayActiveTime = [];
  831.         $tempo = [];
  832.         foreach ($logs as $logBySerie) {
  833.             // Pour chaque Log, nous séparons la chaine de caractère des connexions et des IP en un tableau
  834.             $arrayConnexionIp["S" $i] = explode("|"str_replace("*"" / "$logBySerie->getConnexionIp()));
  835.             // Afin d'éviter les erreurs, nous calculons la durée que si le log d'une série possède une donnée de fin
  836.             if ($logBySerie->getFinish() && $logBySerie->getSerieId() != 11) {
  837.                 // Si la série s'est passée sans accroc, il n'y a donc qu'un seule entrée dans les connexions de la série alors nous calculons simplement le temps passé entre le début et la fin de la série
  838.                 if (count($arrayConnexionIp["S" $i]) <= 1) {
  839.                     $d1 date_create(date('y-m-d H:i:s', (is_numeric($logBySerie->getFirstTime()) ? $logBySerie->getFirstTime() : 0)));
  840.                     $d2 date_create(date('y-m-d H:i:s', (is_numeric($logBySerie->getFinish()) ? $logBySerie->getFinish() : 0)));
  841.                     $diff date_diff($d1$d2);
  842.                     $tempo[$i] = $diff->format("%H:%I:%S");
  843.                     $arrayActiveTime[$i] = $tempo[$i]; // Ici sans déconnexion, le temps de la série est égal au temps actif
  844.                     $totalActiveTime $totalActiveTime + (is_numeric($logBySerie->getFinish()) ? $logBySerie->getFinish() : 0) - (is_numeric($logBySerie->getFirstTime()) ? $logBySerie->getFirstTime() : 0); // On l'ajoute au temps actif total de la série
  845.                 } else {
  846.                     $unco false;
  847.                     $deco = [];
  848.                     // S'il y a au moins une déconnexion faite avec le bouton "déconnecter", nous cherchons le nombre ainsi que la place de celles-ci dans le tableau des connexions de la série
  849.                     foreach ($arrayConnexionIp["S" $i] as $key => $action) {
  850.                         if (substr($action05) == "deco_") {
  851.                             $unco true;
  852.                             array_push($deco$key);
  853.                         }
  854.                     }
  855.                     // Initialisation d'une variable pour la suite
  856.                     $activeTime date_create(date('y-m-d H:i:s'0));
  857.                     // S'il y a au moins une déconnexion faite avec le bouton déconnecter
  858.                     if ($unco == true) {
  859.                         // Pour chaque déconnexion, nous calculons le temps passé entre l'entrée précédant ladite déconnexion (correspondant à l'instant de connexion) et la déconnexion
  860.                         foreach ($deco as $decoKey) {
  861.                             $d1 date_create(substr($arrayConnexionIp["S" $i][$decoKey 1], 017));
  862.                             $d2 date_create(substr($arrayConnexionIp["S" $i][$decoKey], 5));
  863.                             $diff date_diff($d1$d2);
  864.                             $activeTime date_add($activeTime$diff); // Puis on ajoute toutes ces valeurs déterminant ainsi le temps actif
  865.                         }
  866.                     }
  867.                     // S'il y a plusieurs connexion mais pas de déconnexion suite à un arrêt sans cliquer sur le bouton Deconnecter par exemple, alors nous ne comptons que le temps passé depuis la dernière connexion à la série et la fin de la série
  868.                     $date1 date_create(substr($arrayConnexionIp["S" $i][count($arrayConnexionIp["S" $i]) - 1], 017));
  869.                     $date2 date_create(date('y-m-d H:i:s', (is_numeric($logBySerie->getFinish()) ? $logBySerie->getFinish() : 0)));
  870.                     $date1 == false $date1 date_create(date('y-m-d H:i:s'0)) : $date1;
  871.                     $activeTime date_add($activeTimedate_diff($date1$date2));
  872.                     // Puis on ajoute ce temps au temps actif total
  873.                     $arrayActiveTime[$i] = date('H:i:s'date_timestamp_get($activeTime) - 3600);
  874.                     $totalActiveTime $totalActiveTime date_timestamp_get($activeTime);
  875.                     // Et enfin on calcule tout de même le temps entre le début et la fin de la série si jamais il y a besoin de comparer
  876.                     $d1 date_create(date('y-m-d H:i:s', (is_numeric($logBySerie->getFirstTime()) ? $logBySerie->getFirstTime() : 0)));
  877.                     $d2 date_create(date('y-m-d H:i:s', (is_numeric($logBySerie->getFinish()) ? $logBySerie->getFinish() : 0)));
  878.                     $diff date_diff($d1$d2);
  879.                     $tempo[$i] = $diff->format("%H:%I:%S");
  880.                 }
  881.             }
  882.             $i++;
  883.         }
  884.         // Enfin on calcule le temps total en faisant la différence entre le début de la 1ere série et la fin de la 10ème
  885.         if (isset($tenthlog) && $tenthlog->getFinish()) {
  886.             $totalTime date_diff(
  887.                 date_create(date('y-m-d H:i:s', (is_numeric($firstlog->getFirstTime()) ? $firstlog->getFirstTime() : 0))),
  888.                 date_create(date('y-m-d H:i:s', (is_numeric($tenthlog->getFinish()) ? $tenthlog->getFinish() : 0)))
  889.             );
  890.             $totalTime->format("%a") > $totalFormat $totalTime->format("%aj %H:%I:%S") : $totalFormat $totalTime->format("%H:%I:%S");
  891.         }
  892.         return [$tempo$totalActiveTime$arrayConnexionIp$totalFormat$arrayActiveTime];
  893.     }
  894. }