src/Controller/Website/SendExperienceController.php line 71

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Website;
  3. use App\Form\DepartementType;
  4. use App\Form\TerritoryType;
  5. use App\Form\ThematiquesType;
  6. use App\Message\NewExperience;
  7. use Sulu\Bundle\CommunityBundle\Controller\AbstractController;
  8. use Sulu\Bundle\MediaBundle\Collection\Manager\CollectionManager;
  9. use Sulu\Bundle\MediaBundle\Media\Manager\MediaManagerInterface;
  10. use Symfony\Component\Form\Event\PreSubmitEvent;
  11. use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
  12. use Symfony\Component\Form\Extension\Core\Type\DateType;
  13. use Symfony\Component\Form\Extension\Core\Type\EmailType;
  14. use Symfony\Component\Form\Extension\Core\Type\FileType;
  15. use Symfony\Component\Form\Extension\Core\Type\HiddenType;
  16. use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  17. use Symfony\Component\Form\Extension\Core\Type\TelType;
  18. use Symfony\Component\Form\Extension\Core\Type\TextareaType;
  19. use Symfony\Component\Form\Extension\Core\Type\TextType;
  20. use Symfony\Component\Form\Extension\Core\Type\UrlType;
  21. use Symfony\Component\Form\FormEvents;
  22. use Symfony\Component\HttpFoundation\Request;
  23. use Symfony\Component\HttpFoundation\Response;
  24. use Symfony\Component\Messenger\MessageBusInterface;
  25. use Symfony\Component\Routing\RouterInterface;
  26. use Symfony\Component\Security\Core\Security;
  27. use Symfony\Component\Security\Csrf\CsrfToken;
  28. use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
  29. use Symfony\Component\Validator\Constraints as Assert;
  30. use Twig\Environment;
  31. class SendExperienceController extends AbstractController
  32. {
  33.     private Environment $twig;
  34.     private MediaManagerInterface $mediaManager;
  35.     private Security $security;
  36.     private RouterInterface $router;
  37.     private CollectionManager $collectionManager;
  38.     private CsrfTokenManagerInterface $csrfTokenManager;
  39.     /**
  40.      * @var MessageBusInterface $bus
  41.      */
  42.     private MessageBusInterface $bus;
  43.     public function __construct(
  44.         Environment $twig,
  45.         MediaManagerInterface $mediaManager,
  46.         MessageBusInterface $bus,
  47.         Security $security,
  48.         RouterInterface $router,
  49.         CollectionManager $collectionManager,
  50.         CsrfTokenManagerInterface $csrfTokenManager
  51.     ) {
  52.         $this->twig $twig;
  53.         $this->mediaManager $mediaManager;
  54.         $this->bus $bus;
  55.         $this->security $security;
  56.         $this->router $router;
  57.         $this->collectionManager $collectionManager;
  58.         $this->csrfTokenManager $csrfTokenManager;
  59.     }
  60.     public function getExperienceForm(Request $request): Response
  61.     {
  62.         // Cette action est appelée en sous-requête via render(controller()) depuis
  63.         // pages/send-experience.html.twig. FragmentHandler::deliver() rejette toute
  64.         // réponse non-2xx : renvoyer une redirection ici lève une RuntimeException
  65.         // ("Error when rendering ... (Status code is 302)"). On rend donc une
  66.         // invitation à se connecter, en 200.
  67.         if (!$this->security->getUser()) {
  68.             return new Response($this->twig->render('blocks/send-experience-login-required.html.twig', [
  69.                 'loginPath' => $this->router->generate('sulu_community.login'),
  70.             ]));
  71.         }
  72.         $form $this->createFormBuilder(null, ['allow_extra_fields' => true'csrf_protection' => false])
  73.             // first block
  74.             ->add('title'TextType::class, [
  75.                 'required' => true,
  76.                 'label' => 'Titre de l\'expérience*',
  77.                 'attr' => [
  78.                     'placeholder' => 'Titre de l\'expérience',
  79.                     'maxlength' => 150
  80.                 ]
  81.             ])
  82.             ->add('departement_type'DepartementType::class, ['required' => true'label' => ' '])
  83.             ->add('departement'HiddenType::class)
  84.             //{{ form_errors(form.thematiques_type) }}
  85.             // à mettre pour le futur twig du formulaire
  86.             ->add('territory_type'TerritoryType::class, [
  87.                 'required' => false,
  88.                 'label' => 'Choisir les territoires',
  89.             ])
  90.             ->add('territory_select'HiddenType::class, ['mapped' => true])
  91.             ->add('file'FileType::class, [
  92.                 'label' => 'Importer le visuel principal* <span>(PNG, JPG max. 10MB)</span>',
  93.                 'label_html' => true,
  94.                 'required' => true,
  95.                 'constraints' => [
  96.                     new Assert\File(
  97.                         [
  98.                             'maxSize' => '10M',
  99.                             'mimeTypes' => [
  100.                                 'image/jpg',
  101.                                 'image/jpeg',
  102.                                 'image/png'
  103.                             ]
  104.                         ],
  105.                     ),
  106.                 ]
  107.             ])
  108.             ->add('header_img'HiddenType::class, ['mapped' => true])
  109.             ->add('sub_thematiques'TextType::class, [
  110.                 'required' => false,
  111.                 'label' => 'Sous-thématiques',
  112.                 'attr' => [
  113.                     'placeholder' => 'Sous-thématiques'
  114.                 ]
  115.             ])
  116.             ->add('structure'TextType::class, [
  117.                 'required' => true,
  118.                 'label' => 'Nom de la structure porteuse*',
  119.                 'attr' => [
  120.                     'placeholder' => 'Structure'
  121.                 ]
  122.             ])
  123.             ->add('adresse_input'TextType::class, [
  124.                 'required' => true,
  125.                 'label' => 'Adresse*',
  126.                 'attr' => [
  127.                     'placeholder' => 'Adresse'
  128.                 ]
  129.             ])
  130.             ->add('adresse'HiddenType::class)
  131.             ->add('long'HiddenType::class)
  132.             ->add('lat'HiddenType::class)
  133.             // à ajouter
  134.             ->add('phone'TelType::class, [
  135.                 'required' => true,
  136.                 'label' => 'Téléphone*',
  137.                 'attr' => [
  138.                     'placeholder' => 'Téléphone'
  139.                 ]
  140.             ])
  141.             ->add('email'EmailType::class, [
  142.                 'label' => 'Adresse email*',
  143.                 'required' => true,
  144.                 'attr' => [
  145.                     'placeholder' => 'email@exemple.com'
  146.                 ]
  147.             ])
  148.             ->add('personne_rsc'TextType::class, [
  149.                 'required' => false,
  150.                 'label' => 'Nom de la personne ressource',
  151.                 'attr' => [
  152.                     'placeholder' => 'Structure'
  153.                 ]
  154.             ])
  155.             ->add('desc'TextareaType::class, [
  156.                 'required' => true,
  157.                 'label' => 'Résumé de l\'initiative* (600 carac. max)',
  158.                 'attr' => [
  159.                     'placeholder' => 'Réponse',
  160.                     'maxlength' => '600'
  161.                 ]
  162.             ])
  163.             // second block
  164.             ->add('thematiques_type'ThematiquesType::class, [
  165.                 'label' => 'Thématiques*',
  166.                 'required' => true,
  167.                 'constraints' => [
  168.                     new Assert\Callback(function ($data$context) {
  169.                         if (empty($data['name'])) {
  170.                             $context->buildViolation('Veuillez sélectionner au moins une option.')
  171.                                 ->addViolation();
  172.                         }
  173.                     }),
  174.                 ],
  175.             ])
  176.             ->add('thematiques'HiddenType::class)
  177.             // à ajouter
  178.             ->add('status_juridique'TextareaType::class, [
  179.                 'label' => 'Statut juridique (50 carac. max)',
  180.                 'attr' => [
  181.                     'placeholder' => 'Statut juridique',
  182.                     'maxlength' => '50'
  183.                 ],
  184.                 'required' => false
  185.             ])
  186.             // à ajouter
  187.             ->add('influence'ChoiceType::class, [
  188.                 'label' => 'Aire d\'influence*',
  189.                 'multiple' => true,
  190.                 'expanded' => true,
  191.                 'required' => true,
  192.                 'choices' => [
  193.                     'Quartier' => 'Quartier',
  194.                     'Commune' => 'Commune',
  195.                     'Intercommunalité' => 'Intercommunalité',
  196.                     'PNR' => 'PNR',
  197.                     'Pays/PETR' => 'Pays/PETR',
  198.                     'Département' => 'Département',
  199.                     'Région' => 'Région',
  200.                     'Autre' => 'Autre'
  201.                 ],
  202.                 'constraints' => [
  203.                     new Assert\Count([
  204.                         'min' => 1,
  205.                         'minMessage' => 'Veuillez sélectionner au moins une option.',
  206.                     ]),
  207.                 ],
  208.             ])
  209.             ->add('date_start'DateType::class, [
  210.                 'label' => 'Date de lancement*',
  211.                 'required' => true,
  212.             ])
  213.             // à ajouter
  214.             ->add('status'ChoiceType::class, [
  215.                 'label' => 'État d\'achèvement*',
  216.                 'required' => true,
  217.                 'expanded' => true,
  218.                 'choices' => [
  219.                     'En cours' => 'En cours',
  220.                     'Terminé' => 'Terminé'
  221.                 ]
  222.             ])
  223.             // à ajouter
  224.             ->add('cost'TextType::class, [
  225.                 'label' => 'Coût du projet',
  226.                 'attr' => [
  227.                     'placeholder' => 'Coût du projet'
  228.                 ],
  229.                 'required' => false
  230.             ])
  231.             // à ajouter
  232.             ->add('cible'ChoiceType::class, [
  233.                 'label' => 'Public cible*',
  234.                 'multiple' => true,
  235.                 'expanded' => true,
  236.                 'required' => true,
  237.                 'choices' => [
  238.                     'Tout public' => 'Tout public',
  239.                     'Citoyens' => 'Citoyens',
  240.                     'Enfants' => 'Enfants',
  241.                     'Adolescents' => 'Adolescents',
  242.                     'Etudiants' => 'Etudiants',
  243.                     'Seniors' => 'Seniors',
  244.                     'Public en insertion' => 'Public en insertion',
  245.                     'Personnes vulnérables' => 'Personnes vulnérables',
  246.                     'Demandeurs d\'emploi' => 'Demandeurs d\'emploi',
  247.                     'Professionnels' => 'Professionnels',
  248.                     'Agriculteurs' => 'Agriculteurs',
  249.                     'Elus' => 'Elus',
  250.                     'Elus locaux' => 'Elus locaux',
  251.                     'Collectivités territoriales' => 'Collectivités territoriales',
  252.                     'Associations' => 'Associations',
  253.                     'Autre' => 'Autre'
  254.                 ],
  255.                 'constraints' => [
  256.                     new Assert\Count([
  257.                         'min' => 1,
  258.                         'minMessage' => 'Veuillez sélectionner au moins une option.',
  259.                     ]),
  260.                 ],
  261.             ])
  262.             // à ajouter
  263.             ->add('cible_autre'TextareaType::class, [
  264.                 'label' => 'Précisez votre public cible si vous le souhaitez (non obligatoire/100 caractères max)',
  265.                 'required' => false,
  266.                 'attr' => [
  267.                     'placeholder' => 'Réponse',
  268.                     'maxlength' => '100'
  269.                 ]
  270.             ])
  271.             // à ajouter
  272.             ->add('partenaires_techniques'TextareaType::class, [
  273.                 'label' => 'Partenaires techniques (200 carac. max)',
  274.                 'required' => false,
  275.                 'attr' => [
  276.                     'placeholder' => 'Réponse',
  277.                     'maxlength' => '200'
  278.                 ]
  279.             ])
  280.             // à ajouter
  281.             ->add('partenaires_financiers'TextareaType::class, [
  282.                 'label' => 'Partenaires financiers (200 carac. max)',
  283.                 'required' => false,
  284.                 'attr' => [
  285.                     'placeholder' => 'Réponse',
  286.                     'maxlength' => '200'
  287.                 ]
  288.             ])
  289.             // third block
  290.             ->add('compo_equipe'TextareaType::class, [
  291.                 'label' => 'Composition de l\'équipe rattachée au projet (élus, équipe technique, etc.) (200 carac. max)',
  292.                 'required' => false,
  293.                 'attr' => [
  294.                     'placeholder' => 'Réponse',
  295.                     'maxlength' => '200'
  296.                 ]
  297.             ])
  298.             // à ajouter
  299.             ->add('contexte'TextareaType::class, [
  300.                 'label' => 'Contexte de l\'expérience* (1000 carac. max)',
  301.                 'required' => true,
  302.                 'attr' => [
  303.                     'placeholder' => 'Réponse',
  304.                     'maxlength' => '1000'
  305.                 ]
  306.             ])
  307.             ->add('objectif'TextareaType::class, [
  308.                 'label' => 'Objectif de l\'expérience* (1000 carac. max)',
  309.                 'required' => true,
  310.                 'attr' => [
  311.                     'placeholder' => 'Réponse',
  312.                     'maxlength' => '1000'
  313.                 ]
  314.             ])
  315.             // à ajouter
  316.             ->add('mise_oeuvre'TextareaType::class, [
  317.                 'label' => 'Mise en œuvre des actions* (1000 carac. max)',
  318.                 'required' => true,
  319.                 'attr' => [
  320.                     'placeholder' => 'Réponse',
  321.                     'maxlength' => '1000'
  322.                 ]
  323.             ])
  324.             // à ajouter
  325.             ->add('resultats'TextareaType::class, [
  326.                 'label' => 'Résultats (1000 carac. max)',
  327.                 'attr' => [
  328.                     'placeholder' => 'Réponse',
  329.                     'maxlength' => '1000'
  330.                 ],
  331.                 'required' => false
  332.             ])
  333.             // à ajouter
  334.             ->add('facteurs_reussite'TextareaType::class, [
  335.                 'label' => 'Facteurs de réussite* (1000 carac. max)',
  336.                 'required' => true,
  337.                 'attr' => [
  338.                     'placeholder' => 'Réponse',
  339.                     'maxlength' => '1000'
  340.                 ]
  341.             ])
  342.             // à ajouter
  343.             ->add('freins'TextareaType::class, [
  344.                 'label' => 'Freins* (1000 carac. max)',
  345.                 'required' => true,
  346.                 'attr' => [
  347.                     'placeholder' => 'Réponse',
  348.                     'maxlength' => '1000'
  349.                 ]
  350.             ])
  351.             // à ajouter
  352.             ->add('enseignements'TextareaType::class, [
  353.                 'label' => 'Enseignements* (1000 carac. max)',
  354.                 'required' => true,
  355.                 'attr' => [
  356.                     'placeholder' => 'Réponse',
  357.                     'maxlength' => '1000'
  358.                 ]
  359.             ])
  360.             // à ajouter
  361.             ->add('perspectives'TextareaType::class, [
  362.                 'label' => 'Perspectives (1000 carac. max)',
  363.                 'required' => false,
  364.                 'attr' => [
  365.                     'placeholder' => 'Réponse',
  366.                     'maxlength' => '1000'
  367.                 ]
  368.             ])
  369.             // à ajouter
  370.             ->add('link_to_resource'UrlType::class, [
  371.                 'label' => 'Lien vers le site web relatif à la ressource',
  372.                 'attr' => [
  373.                     'placeholder' => 'Lien vers le site web relatif à la ressource'
  374.                 ],
  375.                 'required' => false,
  376.             ])
  377.             // à ajouter
  378.             ->add('other_link'UrlType::class, [
  379.                 'label' => 'Autre lien',
  380.                 'attr' => [
  381.                     'placeholder' => 'Autre lien'
  382.                 ],
  383.                 'required' => false,
  384.             ])
  385.             // à ajouter
  386.             ->add('other_file_input'FileType::class, [
  387.                 'label' => 'Importer un document <span>(PNG, JPG, PDF max. 10MB)</span>',
  388.                 'label_html' => true,
  389.                 'required' => false,
  390.                 'constraints' => [
  391.                     new Assert\File(
  392.                         [
  393.                             'maxSize' => '10M',
  394.                             'mimeTypes' => [
  395.                                 'image/jpg',
  396.                                 'image/jpeg',
  397.                                 'image/png',
  398.                                 'application/pdf'
  399.                             ]
  400.                         ],
  401.                     ),
  402.                 ]
  403.             ])
  404.             ->add('other_file'HiddenType::class, ['mapped' => true])
  405.             ->add('user'HiddenType::class)
  406.             ->add('send'SubmitType::class, ['label' => 'Envoyer'])
  407.             ->addEventListener(FormEvents::PRE_SUBMIT, function (PreSubmitEvent $event): void {
  408.                 $data $event->getData();
  409.                 $file $data['file'];
  410.                 $other_file $data['other_file_input'];
  411.                 if (!array_key_exists('name'$data['thematiques_type'])) {
  412.                     return;
  413.                 }
  414.                 if ($file) {
  415.                     $collectionId null;
  416.                     $collections $this->collectionManager->get('fr');
  417.                     // je boucle sur mes collections et je regarde si il y a une collection avec le titre du document
  418.                     $title 'Gallerie des médias expériences';
  419.                     foreach ($collections as $collection) {
  420.                         if ($title === $collection->getTitle()) {
  421.                             $collectionId $collection->getId();
  422.                         }
  423.                     }
  424.                     if ($collectionId === null) {
  425.                         $collectionData = [
  426.                             "id" => null,
  427.                             "title" => $title,
  428.                             "locale" => 'fr',
  429.                             'type' => ['id' => 1],
  430.                             'parent' => null,
  431.                             'key' => null,
  432.                         ];
  433.                         $currentCollection $this->collectionManager->save($collectionDatanulltrue);
  434.                         $collectionId $currentCollection->getId();
  435.                     }
  436.                     $savedFile $this->mediaManager->save($file, [
  437.                         'locale' => 'fr',
  438.                         'title' => $file->getClientOriginalName(),
  439.                         'collection' => $collectionId
  440.                     ], null);
  441.                     $fileId $savedFile->getId();
  442.                     $data['header_img'] = json_encode(["displayOption" => null"id" => $fileId]);
  443.                     if ($other_file) {
  444.                         $savedFile $this->mediaManager->save($other_file, [
  445.                             'locale' => 'fr',
  446.                             'title' => $other_file->getClientOriginalName(),
  447.                             'collection' => $collectionId
  448.                         ], null);
  449.                         $other_fileId $savedFile->getId();
  450.                         $data['other_file'] = json_encode(["displayOption" => null"id" => $other_fileId]);
  451.                     }
  452.                 } else {
  453.                     $this->addFlash('error''Vous devez renseigner un fichier image de moins de 10M');
  454.                     return;
  455.                 }
  456.                 $data['departement'] = $data['departement_type']['name'];
  457.                 if (isset($data['territory_type']) && array_key_exists('name'$data['territory_type'])) {
  458.                     $data['territory_select'] = json_encode($data['territory_type']['name']);
  459.                 }
  460.                 $data['thematiques'] = json_encode($data['thematiques_type']['name']);
  461.                 $data['adresse'] = json_encode([
  462.                     $data['adresse_input'],
  463.                     $data['lat'],
  464.                     $data['long']
  465.                 ]);
  466.                 $data['user'] = $this->security->getUser()->getContact()->getId(); //marqué comme erreur, mais la fonction est bien présente
  467.                 $event->setData($data);
  468.             })
  469.             ->getForm();
  470.             sleep(3);
  471.         $form->handleRequest($request);
  472.         $data null;
  473.         if ($form->isSubmitted() && $form->isValid()) {
  474.             $data $form->getData(); // Récupérer les données du formulaire si soumis
  475.             $data['thematiques'] = json_decode($data['thematiques']);
  476.             $data['header_img'] = json_decode($data['header_img']);
  477.             $data['other_file'] = json_decode($data['other_file']);
  478.             $data['adresse'] = json_decode($data['adresse']);
  479.             // convertie les ID en INT
  480.             if (isset($data['territory_select']) && null !== $data['territory_select']) {
  481.                 $data['territory_select'] = array_map(
  482.                     function ($value) {
  483.                         return (int)$value;
  484.                     },
  485.                     json_decode($data['territory_select'])
  486.                 );
  487.             }
  488.             unset($data['file']);
  489.             unset($data['departement_type']);
  490.             unset($data['territory_type']);
  491.             unset($data['thematiques_type']);
  492.             unset($data['adresse_input']);
  493.             unset($data['long']);
  494.             unset($data['lat']);
  495.             $this->bus->dispatch(new NewExperience(json_encode(["data" => $this->secureTextData($data)])));
  496.             return new Response($this->twig->render("blocks/send-experience-form.html.twig", [
  497.                 'form' => $form->createView(), // Passez la vue du formulaire
  498.             ]));
  499.         }
  500.         return new Response($this->twig->render("blocks/send-experience-form.html.twig", [
  501.             'form' => $form->createView(), // Passez la vue du formulaire
  502.             'data' => $data// Données soumises (optionnel)
  503.         ]));
  504.     }
  505.     private function secureTextData(array $datas): array
  506.     {
  507.         foreach ($datas as $key => $data) {
  508.             if (is_string($data) && strlen($data) > 0) {
  509.                 $datas[$key] = htmlspecialchars(strip_tags($data), ENT_NOQUOTES ENT_HTML401'UTF-8');
  510.             }
  511.         }
  512.         return $datas;
  513.     }
  514. }