<?php
namespace App\Controller;
use App\Entity\AppelAProjet;
use App\Entity\Project;
use App\Entity\QuestionnaireResponse;
use App\Entity\SignatureCharte;
use App\Entity\User;
use App\Form\AppelAProjetType;
use App\Repository\AppelAProjetRepository;
use App\Repository\ProjectRepository;
use App\Repository\SignatureCharteRepository;
use App\Repository\UserProjectRepository;
use App\Repository\UserRepository;
use App\Service\ProjectNumberService;
use App\Service\ProjectUserService;
use App\Service\QuestionnaireHandlerService;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Vich\UploaderBundle\Handler\DownloadHandler;
/**
* @Route("/appel_a_projet")
*/
class AppelAProjetController extends AbstractController
{
/**
* @Route("/", name="appel_a_projet_index", methods={"GET"})
*/
public function index(AppelAProjetRepository $appelAProjetRepository): Response
{
if ($this->isGranted('ROLE_ADMIN_GIRCI')) {
return $this->render('appel_a_projet/index.html.twig', [
'appel_a_projets' => $appelAProjetRepository->findBy([], ['id' => 'DESC']),
]);
} elseif ($this->isGranted('ROLE_COORDONNATEUR') || $this->isGranted('ROLE_METHODO')) {
return $this->render('appel_a_projet/index.html.twig', [
'appel_a_projets' => $appelAProjetRepository->findPublished(),
]);
// } elseif ($this->getUser() === null) {
// // utilisateur anonyme
// return $this->render('appel_a_projet/index.html.twig', [
// 'appel_a_projets' => $appelAProjetRepository->findPublishedCurrent(),
// ]);
} else {
$appelAProjets = [];
if ($this->isGranted('ROLE_PORTEUR') || $this->isGranted('ROLE_DRCI')) {
// récupération des appels à projets actifs à venir ou courant
$appelAProjets = $appelAProjetRepository->findPublishedCurrent();
// récupération des appels à projet pour lesquels il a déposé un projet
if ($this->isGranted('ROLE_PORTEUR')) {
$appelAProjets = array_replace($appelAProjets, $appelAProjetRepository->findForPorteur($this->getUser()));
}
if ($this->isGranted('ROLE_DRCI')) {
$appelAProjets = array_replace($appelAProjets, $appelAProjetRepository->findForDrci($this->getUser()));
}
}
if ($this->isGranted('ROLE_GIRCI_PARTENAIRE')) {
$appelAProjets = array_replace($appelAProjets, $appelAProjetRepository->findForGirci());
}
if ($this->isGranted('ROLE_PRE_EVAL') || $this->isGranted('ROLE_THEMA') || $this->isGranted('ROLE_EXPERT') || $this->isGranted('ROLE_RAPPORT') || $this->isGranted('ROLE_EXPERT_3')) {
// récupération de tous les appels à projets pour lesquels ils ont des projets associés
$appelAProjets = array_replace($appelAProjets, $appelAProjetRepository->findAssociated($this->getUser()));
}
krsort($appelAProjets);
return $this->render('appel_a_projet/index.html.twig', [
'appel_a_projets' => $appelAProjets
]);
}
}
/**
* @Route("/new", name="appel_a_projet_new", methods={"GET","POST"})
*/
public function new(Request $request, ManagerRegistry $doctrine): Response
{
$this->denyAccessUnlessGranted('CREATE', AppelAProjet::class);
$appelAProjet = new AppelAProjet();
$form = $this->createForm(AppelAProjetType::class, $appelAProjet);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$entityManager = $doctrine->getManager();
$entityManager->persist($appelAProjet);
$entityManager->flush();
$this->addFlash('success', 'Formulaire soumis avec succès');
return $this->redirectToRoute('appel_a_projet_index', [], Response::HTTP_SEE_OTHER);
}
$this->addFlash('error', 'Les données soumises sont invalides');
}
return $this->renderForm('appel_a_projet/new.html.twig', [
'appel_a_projet' => $appelAProjet,
'form' => $form,
]);
}
/**
* @Route("/{id}", name="appel_a_projet_show", methods={"GET"})
*/
public function show(AppelAProjet $appelAProjet): Response
{
return $this->render('appel_a_projet/show.html.twig', [
'appel_a_projet' => $appelAProjet,
]);
}
/**
* @Route("/{id}/edit", name="appel_a_projet_edit", methods={"GET","POST"})
*/
public function edit(Request $request, AppelAProjet $appelAProjet, ManagerRegistry $doctrine): Response
{
$form = $this->createForm(AppelAProjetType::class, $appelAProjet);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$doctrine->getManager()->flush();
$this->addFlash('success', 'Formulaire soumis avec succès');
return $this->redirectToRoute('appel_a_projet_index', [], Response::HTTP_SEE_OTHER);
}
$this->addFlash('error', 'Les donnes soumises sont invalides');
}
return $this->renderForm('appel_a_projet/edit.html.twig', [
'appel_a_projet' => $appelAProjet,
'form' => $form,
]);
}
/**
* @Route("/{id}/ajout-projet", name="appel_a_projet_add_project", methods={"GET", "POST"})
* @Route("/{id}/ajout-projet/auto", name="appel_a_projet_add_project_auto", methods={"POST"})
*
* @param Request $request
* @param AppelAProjet $appelAProjet
* @return Response
*/
public function addProject(Request $request, AppelAProjet $appelAProjet, QuestionnaireHandlerService $questionnaireHandlerService, ProjectNumberService $projectNumberService, ManagerRegistry $doctrine): Response
{
$this->denyAccessUnlessGranted('ADD_PROJECT', $appelAProjet);
if ($appelAProjet->getQuestionnaireLi()) {
$form = $questionnaireHandlerService->createForm($appelAProjet->getQuestionnaireLi());
} else {
$form = $questionnaireHandlerService->createForm($appelAProjet->getQuestionnaireDc());
}
$form->handleRequest($request);
if ($form->isSubmitted()) {
$return = $questionnaireHandlerService->handleSubmission();
if ($return instanceof Response) {
// cas d'une erreur
return $return;
}
// cas où ça c'est bien passé
$questionnaireResponse = $questionnaireHandlerService->getQuestionnaireResponse();
$em = $doctrine->getManager();
/** @var User $user */
$user = $this->getUser();
if ($appelAProjet->getQuestionnaireLi()) {
$questionnaireResponse->setQuestionnaireType(QuestionnaireResponse::QR_TYPE_LI_I);
} else {
$questionnaireResponse->setQuestionnaireType(QuestionnaireResponse::QR_TYPE_DC);
}
$project = new Project();
$project->setAuthor($user)
->setDrci($user->getDrci())
->setStatus(Project::PROJECT_STATUS_BROUILLON)
->setAppelAProjet($appelAProjet)
->setNumber($projectNumberService->generateDraftNumber($project))
->addQuestionnaireResponse($questionnaireResponse)
;
$em->persist($project);
$em->flush();
if ($request->attributes->get('_route') == 'appel_a_projet_add_project') {
$this->addFlash('success', $return);
if ($questionnaireResponse->getStatus() == QuestionnaireResponse::S_STATUS_SUBMIT) {
if ($appelAProjet->getQuestionnaireLi()) {
$this->addFlash('success', 'Vous êtes invité à attacher des documents supplémentaires pour terminer le processus de soumission. Merci de bien vouloir préparer les références bibliographiques justifiant l’intérêt du projet (max 5), le tableur des services co-investigateurs, les CV du porteur et du méthodologiste.');
}
return new JsonResponse(['url' => $this->generateUrl('project_add_files', ['id' => $project->getId()])]);
}
return new JsonResponse(['url' => $this->generateUrl('project_show', ['id' => $project->getId()])]);
} else {
return new JsonResponse(['url' => $this->generateUrl('questionnaire_display_submission', ['questionnaireResponse' => $questionnaireResponse->getId()])]);
}
}
return $questionnaireHandlerService->renderForm($request);
}
/**
* @Route("/{id}/recap-projets", name="appel_a_projet_recap_project", methods={"GET"})
*/
public function recapProjet(AppelAProjet $appelAProjet, ProjectRepository $projectRepository, SignatureCharteRepository $signatureCharteRepository): Response
{
// récupération des projets associés à cet aap et à cet utilisateur, en fonction de son rôle
if ($this->isGranted('ROLE_ADMIN_GIRCI') || $this->isGranted('ROLE_COORDONNATEUR') || $this->isGranted('ROLE_METHODO')) {
return $this->render('project/index.html.twig', [
'appel_a_projet' => $appelAProjet,
'projects' => $projectRepository->findByAap($appelAProjet),
]);
} else {
/** @var User $user */
$user = $this->getUser();
$projects = [];
if ($this->isGranted('ROLE_PRE_EVAL') || $this->isGranted('ROLE_RAPPORT')) {
if ($appelAProjet->getOuvert() === true) {
// todo: accès à tous les projets ? Ou tous les projets pré-évalués dans un premier temps et pour les projets expertisés dans un deuxième ?
// reporter la réponse dans le ProjectVoter
return $this->render('project/index.html.twig', [
'appel_a_projet' => $appelAProjet,
'projects' => $projectRepository->findByAap($appelAProjet),
]);
}
$projects = $projectRepository->findByUserProject($appelAProjet, $user);
}
if ($this->isGranted('ROLE_PORTEUR')) {
$projects = array_replace($projects, $projectRepository->findByPorteur($appelAProjet, $user));
}
if ($this->isGranted('ROLE_DRCI')) {
$projects = array_replace($projects, $projectRepository->findByDrci($appelAProjet, $user));
}
if ($this->isGranted('ROLE_THEMA') || $this->isGranted('ROLE_EXPERT') || $this->isGranted('ROLE_EXPERT_3')){
$projects = array_replace($projects, $projectRepository->findByUserProject($appelAProjet, $user));
}
if ($this->isGranted('ROLE_GIRCI_PARTENAIRE')) {
// récupération des tous les appels à projet ayant un userproject associé au girci de l'utilisateur
$tmpProjects = $projectRepository->findByGirci($appelAProjet, $user->getGirci());
if (count($tmpProjects) > 0) {
// vérification de la signature de la charte (ça ne sert à rien de vérifier la charte si il n'y a pas de projet
$signature = $signatureCharteRepository->findOneBy(['appelAProjet' => $appelAProjet, 'user' => $this->getUser(), 'type' => SignatureCharte::CHARTE_TYPE_GIRCI]);
if (!$signature) {
return $this->redirectToRoute('appel_a_projet_girci_charte', ['id' => $appelAProjet->getId()]);
}
}
$projects = array_replace($projects, $tmpProjects);
}
return $this->render('project/index.html.twig', [
'appel_a_projet' => $appelAProjet,
'projects' => $projects,
]);
}
}
/**
* @Route("/{id}/girci-charte", name="appel_a_projet_girci_charte", methods={"GET", "POST"})
*
* @return Response
*/
public function girciCharte(AppelAProjet $appelAProjet, Request $request, SignatureCharteRepository $signatureCharteRepository, ManagerRegistry $doctrine): Response
{
$this->denyAccessUnlessGranted('ROLE_GIRCI_PARTENAIRE'); // voir si on restraint plus ? Je ne suis pas sûre.
$signature = $signatureCharteRepository->findOneBy(['appelAProjet' => $appelAProjet, 'user' => $this->getUser(), 'type' => SignatureCharte::CHARTE_TYPE_GIRCI]);
if ($signature) {
$this->addFlash('success', 'Vous avez déjà signé cette charte');
return $this->redirectToRoute('appel_a_projet_recap_project', ['id' => $appelAProjet->getId()]);
}
$form = $this->createFormBuilder()
->setAction($request->getRequestUri())
->setMethod('POST')
->getForm()
;
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// enregistrement de la signature
$signature = (new SignatureCharte())
->setAppelAProjet($appelAProjet)
->setUser($this->getUser())
->setType(SignatureCharte::CHARTE_TYPE_GIRCI)
;
$em = $doctrine->getManager();
$em->persist($signature);
$em->flush();
$this->addFlash('success', 'Charte signée avec succès');
return $this->redirectToRoute('appel_a_projet_recap_project', ['id' => $appelAProjet->getId()]);
}
return $this->render('project/signature_charte.html.twig', [
'form' => $form->createView(),
'appelAProjet' => $appelAProjet,
'type' => 'charteGirci',
]);
}
/**
* @Route("/{id}/etat-avancement", name="appel_a_projet_etat_avancement", methods={"GET"})
*
* @param AppelAProjet $appelAProjet
* @param ProjectRepository $projectRepository
*/
public function etatAvancement(AppelAProjet $appelAProjet, ProjectRepository $projectRepository): Response
{
if ($this->isGranted('ROLE_ADMIN_GIRCI')) {
$projects = $projectRepository->getWithUserProjectWithQestionnaireResponse($appelAProjet);
return $this->render('appel_a_projet/etat_avancement.html.twig', [
'appel_a_projet' => $appelAProjet,
'projects' => $projects
]);
}
if ($this->isGranted('ROLE_COORDONNATEUR')) {
// récupération de tous les projets et l'éventuel formulaire dont ils sont l'auteur
$projects = $projectRepository->getWithCoordoQR($appelAProjet, $this->getUser());
return $this->render('appel_a_projet/etat_avancement_coordo.html.twig', [
'appel_a_projet' => $appelAProjet,
'projects' => $projects
]);
}
if ($this->isGranted('ROLE_METHODO')) {
$projects = $projectRepository->findByAap($appelAProjet);
return $this->render('appel_a_projet/etat_avancement_methodo.html.twig', [
'appel_a_projet' => $appelAProjet,
'projects' => $projects
]);
}
throw $this->createAccessDeniedException();
}
/**
* @Route("/{id}/preeval-envoi-email", name="appel_a_projet_preeval_envoi_email", methods={"GET"})
* @Route("/{id}/rapport-envoi-email", name="appel_a_projet_rapport_envoi_email", methods={"GET"})
*
* @param Request $request
* @param AppelAProjet $appelAProjet
* @param UserRepository $userRepository
* @return Response|AccessDeniedException
*/
public function sendEmail(Request $request, AppelAProjet $appelAProjet, UserRepository $userRepository, MailerInterface $mailer, string $emailSenderAddress)
{
switch ($request->attributes->get('_route')) {
case 'appel_a_projet_preeval_envoi_email':
$this->denyAccessUnlessGranted('PREEVAL_SEND_EMAIL', $appelAProjet);
$users = $userRepository->getPreEvalByAap($appelAProjet, [QuestionnaireResponse::QR_TYPE_PREEVAL, QuestionnaireResponse::QR_TYPE_EVAL_THEMA]);
$htmlTemplate = 'email/info-preeval.html.twig';
break;
case 'appel_a_projet_rapport_envoi_email':
$this->denyAccessUnlessGranted('RAPPORT_SEND_EMAIL', $appelAProjet);
$users = $userRepository->getPreEvalByAap($appelAProjet, [QuestionnaireResponse::QR_TYPE_RAPPORT]);
$htmlTemplate = 'email/info-rapport.html.twig';
break;
default:
return $this->createAccessDeniedException();
}
foreach ($users as $user) {
$email = (new TemplatedEmail())
->from($emailSenderAddress)
->to($user->getEmail())
->subject('Bla bla')
->htmlTemplate($htmlTemplate)
->context([
'user' => $user,
'aap' => $appelAProjet,
])
;
$mailer->send($email);
}
$this->addFlash('success', count($users) . ' messages envoyés avec succès');
return $this->redirectToRoute('appel_a_projet_recap_project', ['id' => $appelAProjet->getId()]);
}
/**
* @Route("/{id}/generate-experts", name="appel_a_projet_generate_expert", methods={"GET"})
*
* @param AppelAProjet $appelAProjet
* @param UserProjectRepository $userProjectRepository
* @param ProjectUserService $projectUserService
* @param ManagerRegistry $doctrine
* @return Response
*/
public function generateExperts(AppelAProjet $appelAProjet, ProjectRepository $projectRepository, ProjectUserService $projectUserService, ManagerRegistry $doctrine): Response
{
$this->denyAccessUnlessGranted('GENERATE_EXPERTS', $appelAProjet);
// récupération de tous les projets
$projects = $projectRepository->findByAppelAProjetExpert($appelAProjet);
$em = $doctrine->getManager();
$users = [];
$userProjects = [];
$existingExperts = [];
foreach ($projects as $project) {
foreach ($project->getUserProjects() as $key => $userProject) {
if ($userProject->getUser() == null) {
if (in_array($project->getNumber() . 'A', $existingExperts)) {
$username = $project->getNumber() . 'B';
} else {
$username = $project->getNumber() . 'A';
}
$userProject->setUser($projectUserService->createExpert($userProject->getGirci(), $username));
$users[] = $userProject->getUser();
$userProjects[] = $userProject;
$existingExperts[] = $username;
} else {
$existingExperts[] = $userProject->getUser()->getUserIdentifier();
}
}
}
// je dois générer mon fichier avant d'écrire en base (à cause du erase credential)
$csv = $this->renderView('appel_a_projet/experts.csv.twig', [
'userProjects' => $userProjects
]);
foreach ($users as $user) {
$em->persist($user);
}
$em->flush();
$response = new Response($csv);
$disposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
'experts.csv'
);
$response->headers->set('Content-Disposition', $disposition);
return $response;
}
/**
* Deletes a appel à projet entity.
*
* @Route("/delete/{id}", name="appel_a_projet_delete_confirm", methods={"GET"})
*
* @param AppelAProjet $appelAProjet
* @return Response
*/
public function deleteConfirm(AppelAProjet $appelAProjet): Response
{
$this->denyAccessUnlessGranted('DELETE', $appelAProjet);
return $this->render('appel_a_projet/delete.html.twig', [
'aap' => $appelAProjet,
]);
}
/**
* @Route("/{id}/{charte}", name="appel_a_projet_download_charte", methods={"GET"})
*
* @param AppelAProjet $appelAProjet
* @param $charte
* @param DownloadHandler $downloadHandler
* @return Response
*/
public function downloadCharte(AppelAProjet $appelAProjet, $charte, DownloadHandler $downloadHandler): Response
{
switch ($charte) {
case 'charteGirci':
$filename = $appelAProjet->getCharteGirci()->getOriginalName();
return $downloadHandler->downloadObject($appelAProjet, 'charteGirciFile', null, $filename);
case 'chartePreeval':
$filename = $appelAProjet->getChartePreeval()->getOriginalName();
return $downloadHandler->downloadObject($appelAProjet, 'chartePreevalFile', null, $filename);
case 'charteRapport':
$filename = $appelAProjet->getCharteRapport()->getOriginalName();
return $downloadHandler->downloadObject($appelAProjet, 'charteRapportFile', null, $filename);
case 'charteExpert':
$filename = $appelAProjet->getCharteExpert()->getOriginalName();
return $downloadHandler->downloadObject($appelAProjet, 'charteExpertFile', null, $filename);
case 'charteExpert3':
$filename = $appelAProjet->getCharteExpert3()->getOriginalName();
return $downloadHandler->downloadObject($appelAProjet, 'charteExpert3File', null, $filename);
default:
throw $this->createNotFoundException();
}
}
/**
* @Route("/{id}", name="appel_a_projet_delete", methods={"POST"})
*/
public function delete(Request $request, AppelAProjet $appelAProjet, ManagerRegistry $doctrine, UserProjectRepository $userProjectRepository, SignatureCharteRepository $signatureCharteRepository): Response
{
$this->denyAccessUnlessGranted('DELETE', $appelAProjet);
if ($this->isCsrfTokenValid('delete'.$appelAProjet->getId(), $request->request->get('_token'))) {
$entityManager = $doctrine->getManager();
// suppression des experts
// récupération de tous les userProject de type expert de cet appel à projet
$ups = $userProjectRepository->findByAppelAProjetExpertForDelete($appelAProjet);
foreach ($ups as $up) {
$entityManager->remove($up);
$entityManager->remove($up->getUser());
}
// suppression des signatures de charte
// récupération de toutes les signatures
$signatures = $signatureCharteRepository->findBy(['appelAProjet' => $appelAProjet]);
foreach ($signatures as $signature) {
$entityManager->remove($signature);
}
$entityManager->remove($appelAProjet);
$entityManager->flush();
}
$this->addFlash('success', 'Appel à projet supprimé avec succès.');
return $this->redirectToRoute('appel_a_projet_index', [], Response::HTTP_SEE_OTHER);
}
}