<?php
namespace App\Controller;
use ZipArchive;
use App\Entity\Logs;
use App\Entity\Mail;
use App\Entity\Step;
use App\Entity\User;
use App\Entity\Serie;
use App\Form\UserType;
use DateTimeInterface;
use App\Entity\Answers;
use App\Entity\Question;
use App\Entity\Csvimport;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\DateTime;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
// clef de chiffrement utilisée pour les réponse, elle est cachée là dedans
define("ENCRYPTION_KEY", "2d8e715d3bb3643ce56f5edc724dc1cfb60b226d85cc379b72f35c6d361d071bfa8b9a4f68810373d252cdf0892a959d371da6c230398780f87e74e27917ffce");
class AdminController extends AbstractController
{
// Sert uniquement à avoir une page admin de base
#[Route('/admin', name: 'admin')]
public function index(): Response
{
return $this->render('admin/index.html.twig', [
'controller_name' => 'AdminController',
]);
}
// Cette fonction permet de créer des utilisateurs UN par UN
#[Route('/backoffice/user/create', name: 'user_create')]
public function createUser(Request $request, UserPasswordHasherInterface $userPasswordHasher, ManagerRegistry $manager): Response
{
// Impossible de faire ça sans être admin, même avec le chemin d'accès
$this->denyAccessUnlessGranted("ROLE_ADMIN", null, "Access DENIED");
$entityManager = $manager->getManager();
$user = new User();
$userForm = $this->createForm(UserType::class, $user); //On utilise le formulaire UserType pour créer un utilisateur. (src/Form/UserForm.php)
$userForm->handleRequest($request);
// On vérifie que le formulaire est bien rempli avant d'enregistrer en base de donnée
if ($request->isMethod('post') && $userForm->isValid()) {
$data = $userForm->getData();
switch ($data->getRoles()) {
case "ROLE_ADMIN":
$user->setRoles(['ROLE_ADMIN']);
break;
case "ROLE_USER":
$user->setRoles(['ROLE_USER']);
break;
case ["ROLE_ADMIN", "ROLE_USER"]:
$user->setRoles(['ROLE_ADMIN', 'ROLE_USER']);
break;
}
// 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
$user->setPassword(
$userPasswordHasher->hashPassword(
$user,
$userForm->get('password')->getData()
)
);
$entityManager->persist($user);
$entityManager->flush();
return $this->redirect($this->generateUrl('user_backoffice'));
}
return $this->render('index/dataform.html.twig', [
'formName' => "Création d'utilisateur",
'dataForm' => $userForm->createView(),
"table" => "userCreate",
]);
}
// Cette fonction sert à afficher le tableau des utilisateurs dans le tableau de bord des Admin
#[Route('/backoffice/user', name: 'user_backoffice')]
public function manageUser(ManagerRegistry $manager): Response
{
// Impossible de faire ça sans être admin, même avec le chemin d'accès
$this->denyAccessUnlessGranted("ROLE_ADMIN", null, "Access DENIED");
$entityManager = $manager->getManager();
$userRepository = $entityManager->getRepository(User::class);
$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
// On cherche tous les utilisateurs
$users = $userRepository->findAll();
foreach ($users as $user) {
$logs = $logsRepository->findBy(["user" => $user]);
$firstlog = $logsRepository->findOneBy(["user" => $user, "serieId" => 1]);
$tenthlog = $logsRepository->findOneBy(["user" => $user, "serieId" => 10]);
if (isset($tenthlog)) {
$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
}
}
return $this->render('admin/backofficeUser.html.twig', [
'users' => $users,
'duree' => $duree,
'table' => "user",
'session' => $_SESSION,
]);
}
// Cette fonction sert à afficher le tableau des mails dans le tableau de bord des Admin
#[Route('/backoffice/mail', name: 'mail_backoffice')]
public function manageMail(ManagerRegistry $manager): Response
{
// Impossible de faire ça sans être admin, même avec le chemin d'accès
$this->denyAccessUnlessGranted("ROLE_ADMIN", null, "Access DENIED");
$entityManager = $manager->getManager();
$mailRepository = $entityManager->getRepository(Mail::class);
$mails = $mailRepository->findAll();
return $this->render('admin/backofficeEmail.html.twig', [
'mails' => $mails,
'table' => "mail",
]);
}
// Cette fonction permet de créer des utilisateurs UN par UN
#[Route('/backoffice/mail/create', name: 'mail_create')]
public function createmail(Request $request, ManagerRegistry $manager): Response
{
// Impossible de faire ça sans être admin, même avec le chemin d'accès
$this->denyAccessUnlessGranted("ROLE_ADMIN", null, "Access DENIED");
$entityManager = $manager->getManager();
$mail = new Mail();
$mailForm = $this->createFormBuilder()
->add('code', TextType::class, [
'label' => "Code",
'required' => true,
"attr" => [
'class' => "form-control mb-3"
]
])
->add('adresses', TextType::class, [
'label' => 'Adresse',
'required' => true,
"attr" => [
"class" => "form-control mb-3"
]
])
->add('valider', SubmitType::class, [
'label' => 'Valider',
"attr" => [
"class" => "col-6 form-control btn btn-success mx-auto mt-5",
"type" => "submit"
]
])
->getForm();
//On applique la Requête à notre formulaire
$mailForm->handleRequest($request);
// On vérifie que le formulaire est bien rempli avant d'enregistrer en base de donnée
if ($request->isMethod('post') && $mailForm->isValid()) {
$mail->setCode($mailForm->get('code')->getData());
$mail->setAdresses($mailForm->get('adresses')->getData());
$entityManager->persist($mail);
$entityManager->flush();
return $this->redirect($this->generateUrl('mail_backoffice'));
}
return $this->render('index/dataform.html.twig', [
'formName' => "Création d'utilisateur",
'dataForm' => $mailForm->createView(),
"table" => "mailCreate",
]);
}
// Cette fonction permet de mettre à jour les données des étapes du questionnaire (consignes et rappel de consignes)
#[Route('/backoffice/mail/update-{mailId}', name: 'mail_update')]
public function updateMail(ManagerRegistry $manager, Request $request, int $mailId = 0): Response
{
// Impossible de faire ça sans être admin, même avec le chemin d'accès
$this->denyAccessUnlessGranted("ROLE_ADMIN", null, "Access DENIED");
//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
//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
$entityManager = $manager->getManager();
$etapeRepository = $entityManager->getRepository(Mail::class);
$mail = $etapeRepository->find($mailId);
if (!$mail) {
return $this->redirectToRoute('mail_backoffice');
}
//Si le mail existe, nous créons un formulaire auquel nous lions le mail récupéré
$mailForm = $this->createFormBuilder($mail)
->add('code', TextType::class, [
'label' => "Code",
'required' => true,
"attr" => [
'class' => "form-control mb-3"
]
])
->add('adresses', TextType::class, [
'label' => 'Adresse',
'required' => true,
"attr" => [
"class" => "form-control mb-3"
]
])
->add('valider', SubmitType::class, [
'label' => 'Valider',
"attr" => [
"class" => "col-6 form-control btn btn-success mx-auto mt-5",
"type" => "submit"
]
])
->getForm();
//On applique la Requête à notre formulaire
$mailForm->handleRequest($request);
// On enregistre en base de donnée uniquement si le formulaire est bien rempli
if ($request->isMethod('post') && $mailForm->isValid()) {
$entityManager->persist($mail);
$entityManager->flush();
return $this->redirectToRoute('mail_backoffice');
}
//Si le user n'est pas validé, nous envoyons l'Utilisateur vers la page du formulaire
return $this->render('index/dataform.html.twig', [
'formName' => 'Modification du mail',
'dataForm' => $mailForm->createView(),
'lang' => 'fra',
'table' => 'user',
]);
}
// Cette fonction sert à afficher le tableau des série dans le tableau de bord des Admin
#[Route('/backoffice/serie', name: 'serie_backoffice')]
public function manageSerie(ManagerRegistry $manager): Response
{
// Impossible de faire ça sans être admin, même avec le chemin d'accès
$this->denyAccessUnlessGranted("ROLE_ADMIN", null, "Access DENIED");
$entityManager = $manager->getManager();
$serieRepository = $entityManager->getRepository(Serie::class);
$series = $serieRepository->findAll();
return $this->render('admin/backofficeSerie.html.twig', [
'series' => $series,
'table' => "serie"
]);
}
// Cette fonction sert à afficher le tableau des étapes dans le tableau de bord des Admin
#[Route('/backoffice/step', name: 'step_backoffice')]
public function manageStep(ManagerRegistry $manager): Response
{
// Impossible de faire ça sans être admin, même avec le chemin d'accès
$this->denyAccessUnlessGranted("ROLE_ADMIN", null, "Access DENIED");
$entityManager = $manager->getManager();
$stepRepository = $entityManager->getRepository(Step::class);
$steps = $stepRepository->findAll();
return $this->render('admin/backofficeStep.html.twig', [
'steps' => $steps,
'table' => "step"
]);
}
// Cette fonction sert à afficher le tableau des Questions et Réponses dans le tableau de bord des Admin
#[Route('/backoffice/question', name: 'question_backoffice')]
public function manageQuestion(ManagerRegistry $manager): Response
{
// Impossible de faire ça sans être admin, même avec le chemin d'accès
$this->denyAccessUnlessGranted("ROLE_ADMIN", null, "Access DENIED");
$entityManager = $manager->getManager();
$questionRepository = $entityManager->getRepository(Question::class);
$questions = $questionRepository->findAll();
return $this->render('admin/backofficeQuestion.html.twig', [
'questions' => $questions,
'table' => "question"
]);
}
// function ExportCSV(){
// $this->load->dbutil();
// $this->load->helper('file');
// $this->load->helper('download');
// $delimiter = ";";
// $newline = "\r\n";
// $filename = "mpfusers_".date('Y-m-d').".csv";
// $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";
// $result = $this->db->query($query);
// $data = $this->dbutil->csv_from_result($result, $delimiter, $newline);
// force_download($filename, $data);
// }
// Cette fonction sert à afficher les logs d'un utilisateur dans le tableau de bord des Admin
#[Route('/backoffice/logs/user-{userId}', name: 'user_logs')]
public function userLogs(ManagerRegistry $manager, $userId = 1): Response
{
// Impossible de faire ça sans être admin, même avec le chemin d'accès
$this->denyAccessUnlessGranted("ROLE_ADMIN", null, "Access DENIED");
$entityManager = $manager->getManager();
$userRepository = $entityManager->getRepository(User::class);
$logsRepository = $entityManager->getRepository(Logs::class);
$user = $userRepository->find($userId);
$logs = $logsRepository->findBy(["user" => $user]);
$firstlog = $logsRepository->findOneBy(["user" => $user, "serieId" => 1]);
$tenthlog = $logsRepository->findOneBy(["user" => $user, "serieId" => 10]);
if (!$logs) {
return $this->render("admin/backofficeUserLogs.html.twig", [
"user" => $user,
"logs" => "vide",
"table" => "user"
]);
}
// Tout se passe ici, cette méthode est créée plus bas
$resultat = $this->activeTime($logs, $firstlog, $tenthlog);
return $this->render("admin/backofficeUserLogs.html.twig", [
"user" => $user,
"logs" => $logs,
"nlogs" => count($logs),
"duree" => $resultat[0],
"active" => $resultat[4],
"totalActiveTime" => date('H:i:s', $resultat[1] - 3600),
"totalTime" => $resultat[3],
"arrayIP" => $resultat[2],
"table" => "user"
]);
}
// Cette fonction sert à mettre à jour un utilisateur dans la base de donnée
#[Route('/backoffice/user/update-{userId}', name: 'user_update')]
public function updateUser(ManagerRegistry $manager, Request $request, UserPasswordHasherInterface $userPasswordHasher, int $userId = 0): Response
{
// Impossible de faire ça sans être admin, même avec le chemin d'accès
$this->denyAccessUnlessGranted("ROLE_ADMIN", null, "Access DENIED");
//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
//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
$entityManager = $manager->getManager();
$userRepository = $entityManager->getRepository(User::class);
$etapeRepository = $entityManager->getRepository(Step::class);
$serieRepository = $entityManager->getRepository(Serie::class);
//Nous récupérons le user dont l'ID est renseigné
$user = $userRepository->find($userId);
//Si aucune user n'est trouvé, $user vaut null et nous retournons à l'index
//En mettant à la fonction, on redirige l'Utilisateur et on évite les erreurs
if (!$user) {
return $this->redirectToRoute('user_backoffice');
}
//Si le user existe, nous créons un formulaire auquel nous lions le user récupéré
$userForm = $this->createFormBuilder($user)
->add('username', TextType::class, [
'label' => "Login",
'required' => false,
"attr" => [
'class' => "form-control mb-3"
]
])
->add('name', TextType::class, [
'label' => 'Nom',
'required' => false,
"attr" => [
"class" => "form-control mb-3"
]
])
->add('roles', ChoiceType::class, [
'label' => "Role",
'required' => false,
"choices" => [
'Utilisateur.........................................................................................' => 'ROLE_USER',
"Admin" => 'ROLE_ADMIN'
],
'expanded' => true,
'multiple' => true,
"attr" => [
'class' => "justify-content-evenly mb-3"
]
])
->add('password', PasswordType::class, [
// instead of being set onto the object directly,
// this is read and encoded in the controller
'label' => "Nouveau mot de passe",
'mapped' => false,
'required' => false,
"attr" => [
'class' => "form-control mb-3",
'autocomplete' => 'new-password'
],
'constraints' => [
new Length([
'min' => 4,
'minMessage' => 'Your password should be at least {{ limit }} characters',
// max length allowed by Symfony for security reasons
'max' => 4096,
]),
]
])
->add('currentSerie', NumberType::class, [
'label' => 'Serie en cours',
'required' => false,
"attr" => [
"class" => "form-control mb-3"
]
])
->add('infos', TextType::class, [
'label' => 'infos',
'required' => false,
"attr" => [
"class" => "form-control mb-3"
]
])
->add('sexe', TextType::class, [
'label' => 'sexe',
'required' => false,
"attr" => [
"class" => "form-control mb-3"
]
])
->add('age', NumberType::class, [
'label' => 'age',
'required' => false,
"attr" => [
"class" => "form-control mb-3"
]
])
->add('valider', SubmitType::class, [
'label' => 'Valider',
"attr" => [
"class" => "col-6 form-control btn btn-success mx-auto mt-5",
"type" => "submit"
]
])
->getForm();
//On applique la Requête à notre formulaire
$userForm->handleRequest($request);
//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
if ($request->isMethod('post') && $userForm->isValid()) {
//Nous vérifions l'absence de user au même nom dans notre BDD
$userDuplicata = $userRepository->findOneBy(['name' => $user->getName()]);
if ($userDuplicata && ($userDuplicata->getId() != $user->getId())) { //Si un doublon (qui n'est pas notre produit déjà présent) est trouvé
//Nous signifions qu'une entrée identique existe
////////////////////////// Message d'erreur à remplir
} else {
if ($userForm->get('password')->getData() != null) {
$user->setPassword(
$userPasswordHasher->hashPassword($user, $userForm->get('password')->getData())
);
}
$user->setCurrentStep($etapeRepository->findOneBy([
"serie" => $serieRepository->find($user->getCurrentSerie()),
"number" => 1
])->getId());
$entityManager->persist($user);
$entityManager->flush();
}
return $this->redirectToRoute('user_backoffice');
}
//Si le user n'est pas validé, nous envoyons l'Utilisateur vers la page du formulaire
return $this->render('index/dataform.html.twig', [
'formName' => 'Modification du user',
'dataForm' => $userForm->createView(),
'lang' => 'fra',
'table' => 'user',
]);
}
// Cette fonction permet de cacher ou non les utilisateurs dans le tableau de bord des Admins
#[Route('/backoffice/user/hideUser', name: 'user_hide')]
public function hideUser(ManagerRegistry $manager): Response
{
// Impossible de faire ça sans être admin, même avec le chemin d'accès
$this->denyAccessUnlessGranted("ROLE_ADMIN", null, "Access DENIED");
$entityManager = $manager->getManager();
$userRepository = $entityManager->getRepository(User::class);
$cookie = explode("|", $_COOKIE['hiddenUsers']);
foreach ($cookie as $key => $value) {
$temp = explode("*", $value);
$arrayCookie[$temp[0]] = $temp[1];
}
foreach ($arrayCookie as $id => $hide) {
$user = $userRepository->find($id);
if ($user) {
$user->setHidden($hide);
$entityManager->persist($user);
}
}
$entityManager->flush();
return $this->redirectToRoute('user_backoffice');
}
// Cette fonction permet de mettre à jour les données des étapes du questionnaire (consignes et rappel de consignes)
#[Route('/backoffice/step/update-{stepId}', name: 'step_update')]
public function updateStep(ManagerRegistry $manager, int $stepId = 0): Response
{
// Impossible de faire ça sans être admin, même avec le chemin d'accès
$this->denyAccessUnlessGranted("ROLE_ADMIN", null, "Access DENIED");
//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
//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
$entityManager = $manager->getManager();
$etapeRepository = $entityManager->getRepository(Step::class);
$serieRepository = $entityManager->getRepository(Serie::class);
$step = $etapeRepository->find($stepId);
$serie = $serieRepository->find($step->getSerie()->getId());
// On enregistre en base de donnée uniquement si le formulaire est bien rempli
if (isset($_POST) && isset($_POST["valider"])) {
$step->setDescription($_POST["content"]);
isset($_POST["step-progress"]) ? $step->setProgress($_POST["step-progress"]) : null;
isset($_POST["step-number"]) ? $step->setNumber($_POST["step-number"]) : null;
$entityManager->persist($step);
$entityManager->flush();
return $this->redirectToRoute('step_backoffice');
}
return $this->render('admin/backofficeEditor.html.twig', [
'serie' => $serie,
'target' => 'step',
'step' => $step,
'content' => $step->getDescription(),
'previousPage' => "/backoffice/step/update-" . $stepId,
'table' => "etape",
]);
}
// Cette fonction permet de mettre à jour les questions et les réponses du questionnaire
#[Route('/backoffice/question/update-{questionId}', name: 'question_update')]
public function updateQuestion(ManagerRegistry $manager, int $questionId = 0): Response
{
// Impossible de faire ça sans être admin, même avec le chemin d'accès
$this->denyAccessUnlessGranted("ROLE_ADMIN", null, "Access DENIED");
$entityManager = $manager->getManager();
$questionRepository = $entityManager->getRepository(Question::class);
$serieRepository = $entityManager->getRepository(Serie::class);
$question = $questionRepository->find($questionId);
$serie = $serieRepository->find($question->getSerie()->getId());
// On enregistre en base de donnée uniquement si le formulaire est bien rempli
if (isset($_POST) && isset($_POST["valider"])) {
$question->setContent($_POST["question-content"]);
isset($_POST["question-content"]) ? $question->setContent($_POST["question-content"]) : null;
isset($_POST["question-serie"]) ? $question->setSerie($serieRepository->find($_POST["question-serie"])) : null;
isset($_POST["question-type"]) ? $question->setType($_POST["question-type"]) : null;
foreach ($question->getChoices() as $choice) {
isset($_POST["choice-number-" . $choice->getNumber()]) ? $choice->setNumber($_POST["choice-number-" . $choice->getNumber()]) : null;
isset($_POST["choice-content-" . $choice->getNumber()]) ? $choice->setNumber($_POST["choice-content-" . $choice->getNumber()]) : null;
$entityManager->persist($choice);
}
$entityManager->persist($question);
$entityManager->flush();
return $this->redirectToRoute('question_backoffice');
}
return $this->render('admin/backofficeEditor.html.twig', [
'serie' => $serie,
'target' => 'question',
'question' => $question,
'choices' => $question->getChoices(),
'content' => $question->getContent(),
'previousPage' => "/backoffice/question/update-" . $questionId,
'table' => "etape",
]);
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////// Cryptage //////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
// Cette méthode est là pour le chiffrement des réponses
private function jencrypt($text, $key)
{
// $text = serialize($text);
$key2 = sodium_hex2bin(substr($key, 0, 64)); // 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
$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.
$encoded = base64_encode($encrypt); // 2eme chiffrement avec une méthode dite "de base 64" pour plus de sécurité
return $encoded;
}
// 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
#[Route('/backoffice/reponsetxt/{userId}', name: 'reponse')]
public function getAnswersTxt(ManagerRegistry $manager, $userId): Response
{
$entityManager = $manager->getManager();
$answersRepository = $entityManager->getRepository(Answers::class);
$userRepository = $entityManager->getRepository(User::class);
$user = $userRepository->findOneBy(["id" => $userId]); // On selectionne l'utilisateur sur lequel on a cliqué
$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
$corps = ""; //Notre texte chiffré, que l'on va ajouter petit à petit
$corps .= $this->jencrypt("# **************************** \r\n", ENCRYPTION_KEY) . "\r\n"; // Ajout d'une ligne de séparation chiffrée
$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
foreach ($reponses as $row) {
// Ajout de chaque série de réponse
$corps .= $this->jencrypt("\r\n# **************************** \r\n", ENCRYPTION_KEY) . "\r\n";
$corps .= $this->jencrypt("# serie " . $row->getSerie()->getId() . " \r\n", ENCRYPTION_KEY) . "\r\n";
$corps .= $this->jencrypt("# **************************** \r\n", ENCRYPTION_KEY) . "\r\n";
$corps .= $row->getFlow();
}
// Ici on déclenche le téléchargement du fichier texte qui vient d'être créé avec les réponses
$this->force_download($user->getUsername() . '-crypte.txt', $corps);
// On revient au tableau des utilisateurs
return $this->redirectToRoute('user_backoffice');
}
// 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
#[Route('/backoffice/download', name: 'user_dl')]
public function downloadTxt(ManagerRegistry $manager): Response
{
// Impossible de faire ça sans être admin, même avec le chemin d'accès
$this->denyAccessUnlessGranted("ROLE_ADMIN", null, "Access DENIED");
ob_start();
$entityManager = $manager->getManager();
$answersRepository = $entityManager->getRepository(Answers::class);
$userRepository = $entityManager->getRepository(User::class);
$cookie = explode("|", $_COOKIE['toDownload']);
$arrayFiles = [];
# create new zip opbject
$zip = new ZipArchive();
# create a temp file & open it
$tmp_zip = tempnam(".",'');
$zip->open($tmp_zip, ZipArchive::CREATE);
foreach ($cookie as $userId) {
$user = $userRepository->find($userId);
if ($user) {
$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
$corps = ""; //Notre texte chiffré, que l'on va ajouter petit à petit
$corps .= $this->jencrypt("# **************************** \r\n", ENCRYPTION_KEY) . "\r\n"; // Ajout d'une ligne de séparation chiffrée
$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
foreach ($answers as $row) {
// Ajout de chaque série de réponse
$corps .= $this->jencrypt("\r\n# **************************** \r\n", ENCRYPTION_KEY) . "\r\n";
$corps .= $this->jencrypt("# serie " . $row->getSerie()->getId() . " \r\n", ENCRYPTION_KEY) . "\r\n";
$corps .= $this->jencrypt("# **************************** \r\n", ENCRYPTION_KEY) . "\r\n";
$corps .= $row->getFlow();
}
$corps = trim($corps);
$userName = $user->getUsername();
// $tmp_file = tempnam('.', $userName . '-crypte.txt');
array_push($arrayFiles, $userName . '-crypte.txt');
file_put_contents($userName . '-crypte.txt', $corps);
$zip->addFile($userName . '-crypte.txt');
$user->setDownloaded(1);
$entityManager->persist($user);
$entityManager->flush();
}
}
$zipsize = $zip->statName("size");
$zip->close();
ob_end_flush();
foreach ($arrayFiles as $file) {
unlink($file);
}
$this->force_download_2(date("Ymj_Hi") . '_lots_myBim-crypte.zip', file_get_contents($tmp_zip), $tmp_zip);
// On revient au tableau des utilisateurs
return $this->redirectToRoute('user_backoffice');
}
/**
* Force Download
*
* Generates headers that force a download to happen
*
* @param string filename
* @param mixed the data to be downloaded
* @param bool whether to try and send the actual file MIME type
* @return void
*/
public function force_download($filename = '', $data = '', $set_mime = FALSE)
{
if ($filename === '' or $data === '') {
return;
} elseif ($data === NULL) {
if (!@is_file($filename) or ($filesize = @filesize($filename)) === FALSE) {
return;
}
$filepath = $filename;
$filename = explode('/', str_replace(DIRECTORY_SEPARATOR, '/', $filename));
$filename = end($filename);
} else {
// $filesize = strlen($data);
}
// Set the default MIME type to send
$mime = 'application/octet-stream';
$x = explode('.', $filename);
$extension = end($x);
if ($set_mime === TRUE) {
if (count($x) === 1 or $extension === '') {
/* If we're going to detect the MIME type,
* we'll need a file extension.
*/
return;
}
// Load the mime types
$mimes = &$this->get_mimes();
// Only change the default MIME if we can find one
if (isset($mimes[$extension])) {
$mime = is_array($mimes[$extension]) ? $mimes[$extension][0] : $mimes[$extension];
}
}
/* It was reported that browsers on Android 2.1 (and possibly older as well)
* need to have the filename extension upper-cased in order to be able to
* download it.
*
* Reference: http://digiblog.de/2011/04/19/android-and-the-download-file-headers/
*/
if (count($x) !== 1 && isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/Android\s(1|2\.[01])/', $_SERVER['HTTP_USER_AGENT'])) {
$x[count($x) - 1] = strtoupper($extension);
$filename = implode('.', $x);
}
if ($data === NULL && ($fp = @fopen($filepath, 'rb')) === FALSE) {
return;
}
// Clean output buffer
if (ob_get_level() !== 0 && @ob_end_clean() === FALSE) {
@ob_clean();
}
// Generate the server headers
header('Content-Type: ' . $mime);
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Expires: 0');
header('Content-Transfer-Encoding: binary');
// header('Content-Length: ' . $filesize);
header('Cache-Control: private, no-transform, no-store, must-revalidate');
// If we have raw data - just dump it
if ($data !== NULL) {
exit($data);
}
// Flush 1MB chunks of data
while (!feof($fp) && ($data = fread($fp, 1048576)) !== FALSE) {
echo $data;
}
fclose($fp);
exit;
}
public function force_download_2($filename = '', $data = '', $filePlace, $set_mime = FALSE)
{
if ($filename === '' or $data === '') {
return;
} elseif ($data === NULL) {
if (!@is_file($filename) or ($filesize = @filesize($filename)) === FALSE) {
return;
}
$filepath = $filename;
$filename = explode('/', str_replace(DIRECTORY_SEPARATOR, '/', $filename));
$filename = end($filename);
} else {
// $filesize = strlen($data);
}
// Set the default MIME type to send
$mime = 'application/octet-stream';
$x = explode('.', $filename);
$extension = end($x);
if ($set_mime === TRUE) {
if (count($x) === 1 or $extension === '') {
/* If we're going to detect the MIME type,
* we'll need a file extension.
*/
return;
}
// Load the mime types
$mimes = &$this->get_mimes();
// Only change the default MIME if we can find one
if (isset($mimes[$extension])) {
$mime = is_array($mimes[$extension]) ? $mimes[$extension][0] : $mimes[$extension];
}
}
/* It was reported that browsers on Android 2.1 (and possibly older as well)
* need to have the filename extension upper-cased in order to be able to
* download it.
*
* Reference: http://digiblog.de/2011/04/19/android-and-the-download-file-headers/
*/
if (count($x) !== 1 && isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/Android\s(1|2\.[01])/', $_SERVER['HTTP_USER_AGENT'])) {
$x[count($x) - 1] = strtoupper($extension);
$filename = implode('.', $x);
}
if ($data === NULL && ($fp = @fopen($filepath, 'rb')) === FALSE) {
return;
}
// Clean output buffer
if (ob_get_level() !== 0 && @ob_end_clean() === FALSE) {
@ob_clean();
}
// Generate the server headers
// header('Content-Type: octet-stream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Expires: 0');
header('Content-Transfer-Encoding: binary');
// header('Content-Length: ' . $filesize);
header('Cache-Control: private, no-transform, no-store, must-revalidate');
unlink($filePlace);
// If we have raw data - just dump it
if ($data !== NULL) {
exit($data);
}
// Flush 1MB chunks of data
while (!feof($fp) && ($data = fread($fp, 1048576)) !== FALSE) {
echo $data;
}
fclose($fp);
// exit;
}
/**
* Returns the MIME types array from config/mimes.php
*
* @return array
*/
public function &get_mimes()
{
static $_mimes;
if (empty($_mimes)) {
$_mimes = array();
}
return $_mimes;
}
///////////////////////// Import CSV //////////////////////////
// Cette fonction permet d'importer les fichiers CSV et ajouter des utilisateurs à la base de données
#[Route('/backoffice/importCSV', name: 'importCSV')]
function import(UserPasswordHasherInterface $userPasswordHasher, ManagerRegistry $manager): Response
{
if ($_FILES) {
if ($_FILES['file']['name'] != "") {
$entityManager = $manager->getManager();
$fileName = $_FILES['file']['tmp_name'];
$fileHandle = fopen($fileName, "r") or exit("Impossible d'ouvrir le fichier !");
//On boucle sur toutes les lignes du document CSV.
while (($row = fgetcsv($fileHandle, 0, ";")) !== FALSE) {
// 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 - .
if ($row[0] != "login") {
$user = new User;
$user->setUsername($row[0]);
$user->setPassword(
$userPasswordHasher->hashPassword($user, $row[1])
);
$user->setName($row[2]);
$entityManager->persist($user);
}
}
$entityManager->flush();
}
}
return $this->redirectToRoute('user_backoffice');
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// 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
public function activeTime($logs, $firstlog, $tenthlog)
{
// Initialisation des variables
$i = 1;
$totalActiveTime = 0;
$totalTime = 0;
$totalFormat = 0;
$arrayActiveTime = [];
$tempo = [];
foreach ($logs as $logBySerie) {
// Pour chaque Log, nous séparons la chaine de caractère des connexions et des IP en un tableau
$arrayConnexionIp["S" . $i] = explode("|", str_replace("*", " / ", $logBySerie->getConnexionIp()));
// 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
if ($logBySerie->getFinish() && $logBySerie->getSerieId() != 11) {
// 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
if (count($arrayConnexionIp["S" . $i]) <= 1) {
$d1 = date_create(date('y-m-d H:i:s', (is_numeric($logBySerie->getFirstTime()) ? $logBySerie->getFirstTime() : 0)));
$d2 = date_create(date('y-m-d H:i:s', (is_numeric($logBySerie->getFinish()) ? $logBySerie->getFinish() : 0)));
$diff = date_diff($d1, $d2);
$tempo[$i] = $diff->format("%H:%I:%S");
$arrayActiveTime[$i] = $tempo[$i]; // Ici sans déconnexion, le temps de la série est égal au temps actif
$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
} else {
$unco = false;
$deco = [];
// 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
foreach ($arrayConnexionIp["S" . $i] as $key => $action) {
if (substr($action, 0, 5) == "deco_") {
$unco = true;
array_push($deco, $key);
}
}
// Initialisation d'une variable pour la suite
$activeTime = date_create(date('y-m-d H:i:s', 0));
// S'il y a au moins une déconnexion faite avec le bouton déconnecter
if ($unco == true) {
// 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
foreach ($deco as $decoKey) {
$d1 = date_create(substr($arrayConnexionIp["S" . $i][$decoKey - 1], 0, 17));
$d2 = date_create(substr($arrayConnexionIp["S" . $i][$decoKey], 5));
$diff = date_diff($d1, $d2);
$activeTime = date_add($activeTime, $diff); // Puis on ajoute toutes ces valeurs déterminant ainsi le temps actif
}
}
// 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
$date1 = date_create(substr($arrayConnexionIp["S" . $i][count($arrayConnexionIp["S" . $i]) - 1], 0, 17));
$date2 = date_create(date('y-m-d H:i:s', (is_numeric($logBySerie->getFinish()) ? $logBySerie->getFinish() : 0)));
$date1 == false ? $date1 = date_create(date('y-m-d H:i:s', 0)) : $date1;
$activeTime = date_add($activeTime, date_diff($date1, $date2));
// Puis on ajoute ce temps au temps actif total
$arrayActiveTime[$i] = date('H:i:s', date_timestamp_get($activeTime) - 3600);
$totalActiveTime = $totalActiveTime + date_timestamp_get($activeTime);
// 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
$d1 = date_create(date('y-m-d H:i:s', (is_numeric($logBySerie->getFirstTime()) ? $logBySerie->getFirstTime() : 0)));
$d2 = date_create(date('y-m-d H:i:s', (is_numeric($logBySerie->getFinish()) ? $logBySerie->getFinish() : 0)));
$diff = date_diff($d1, $d2);
$tempo[$i] = $diff->format("%H:%I:%S");
}
}
$i++;
}
// 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
if (isset($tenthlog) && $tenthlog->getFinish()) {
$totalTime = date_diff(
date_create(date('y-m-d H:i:s', (is_numeric($firstlog->getFirstTime()) ? $firstlog->getFirstTime() : 0))),
date_create(date('y-m-d H:i:s', (is_numeric($tenthlog->getFinish()) ? $tenthlog->getFinish() : 0)))
);
$totalTime->format("%a") > 0 ? $totalFormat = $totalTime->format("%aj %H:%I:%S") : $totalFormat = $totalTime->format("%H:%I:%S");
}
return [$tempo, $totalActiveTime, $arrayConnexionIp, $totalFormat, $arrayActiveTime];
}
}