<?php /** * This file is part of the MADIS - RGPD Management application. * * @copyright Copyright (c) 2018-2019 Soluris - Solutions Numériques Territoriales Innovantes * @author Donovan Bourlard <donovan@awkan.fr> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ declare(strict_types=1); namespace App\Domain\Maturity\Controller; use App\Application\Controller\CRUDController; use App\Application\Symfony\Security\UserProvider; use App\Application\Traits\ServersideDatatablesTrait; use App\Domain\Maturity\Form\Type\ImportModeleType; use App\Domain\Maturity\Form\Type\ModeleReferentielRightsType; use App\Domain\Maturity\Form\Type\ReferentielType; use App\Domain\Maturity\Model; use App\Domain\Maturity\Repository; use Doctrine\ORM\EntityManagerInterface; use JMS\Serializer\SerializerBuilder; use Knp\Snappy\Pdf; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** * @property Repository\Referentiel $repository */ class ReferentielController extends CRUDController { use ServersideDatatablesTrait; /** * @var AuthorizationCheckerInterface */ protected $authorizationChecker; /** * @var UserProvider */ protected $userProvider; protected RouterInterface $router; public function __construct( EntityManagerInterface $entityManager, TranslatorInterface $translator, Repository\Referentiel $repository, AuthorizationCheckerInterface $authorizationChecker, UserProvider $userProvider, RouterInterface $router, Pdf $pdf, ) { parent::__construct($entityManager, $translator, $repository, $pdf, $userProvider, $authorizationChecker); $this->authorizationChecker = $authorizationChecker; $this->userProvider = $userProvider; $this->router = $router; } protected function getDomain(): string { return 'maturity'; } protected function getModel(): string { return 'referentiel'; } protected function getModelClass(): string { return Model\Referentiel::class; } protected function getFormType(): string { return ReferentielType::class; } /** * {@inheritdoc} * Override method in order to hydrate survey answers. */ public function createAction(Request $request): Response { /** * @var Model\Referentiel */ $modelClass = $this->getModelClass(); $object = new $modelClass(); $form = $this->createForm($this->getFormType(), $object); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $this->formPrePersistData($object); $this->entityManager->persist($object); $this->entityManager->flush(); $this->addFlash('success', $this->getFlashbagMessage('success', 'create', $object->getName())); return $this->redirectToRoute($this->getRouteName('list')); } return $this->render($this->getTemplatingBasePath('create'), [ 'form' => $form->createView(), ]); } public function editAction(Request $request, string $id): Response { /** @var Model\Referentiel $object */ $object = $this->repository->findOneById($id); if (!$object) { throw new NotFoundHttpException("No object found with ID '{$id}'"); } $sections = $this->entityManager->getRepository(Model\Domain::class)->findBy(['referentiel' => $object]); $form = $this->createForm($this->getFormType(), $object, ['validation_groups' => ['default', $this->getModel(), 'edit']]); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $this->formPrePersistData($object); $this->entityManager->persist($object); $this->entityManager->flush(); $this->addFlash('success', $this->getFlashbagMessage('success', 'edit', $object)); return $this->redirectToRoute($this->getRouteName('list')); } return $this->render($this->getTemplatingBasePath('edit'), [ 'form' => $form->createView(), ]); } public function duplicateAction(Request $request, string $id): Response { /** @var Model\Referentiel $object */ $object = $this->repository->findOneById($id); if (!$object) { throw new NotFoundHttpException("No object found with ID '{$id}'"); } $newRef = new Model\Referentiel(); $newRef->setName($object->getName()); $newRef->setDescription($object->getDescription()); $newRef->setAuthorizedCollectivities($object->getAuthorizedCollectivities()); $newRef->setAuthorizedCollectivityTypes($object->getAuthorizedCollectivityTypes()); $newRef->setOptionRightSelection($newRef->getOptionRightSelection()); /** @var Model\Domain $domain */ foreach ($object->getDomains() as $domain) { $d = new Model\Domain(); $d->setName($domain->getName()); $d->setDescription($domain->getDescription()); $d->setReferentiel($newRef); $d->setColor($domain->getColor()); $d->setPosition($domain->getPosition()); $this->entityManager->persist($d); /** @var Model\Question $q */ foreach ($domain->getQuestions() as $q) { $newQ = new Model\Question(); $newQ->setPosition($q->getPosition()); $newQ->setName($q->getName()); $newQ->setOptional($q->getOptional()); if ($q->getOptional()) { $newQ->setOptionReason($q->getOptionReason()); } $newQ->setWeight($q->getWeight()); $newQ->setDomain($d); $this->entityManager->persist($newQ); /** @var Model\Answer $a */ foreach ($q->getAnswers() as $a) { $newA = new Model\Answer(); $newA->setName($a->getName()); $newA->setPosition($a->getPosition()); $newA->setRecommendation($a->getRecommendation()); $newA->setResponse($a->getResponse()); $newA->setQuestion($newQ); $this->entityManager->persist($newA); } } } $this->entityManager->persist($newRef); $this->entityManager->flush(); $this->addFlash('success', $this->getFlashbagMessage('success', 'duplicate', $object)); return $this->redirectToRoute($this->getRouteName('edit'), ['id' => $newRef->getId()->toString()]); } public function formPrePersistData($object) { $domains = []; $colors = [ 'info', 'success', 'primary', 'warning', ]; // get all existing domains $toRemove = $this->entityManager->getRepository(Model\Domain::class)->findBy(['referentiel' => $object]); foreach ($object->getDomains() as $k => $domain) { $key = array_search($domain, $toRemove); if (false !== $key) { unset($toRemove[$key]); } /* @var Model\Domain $domain */ // $domain->setPosition($k); $domain->setColor($colors[$k % 4]); // get all existing questions $toRemoveQuestions = $this->entityManager->getRepository(Model\Question::class)->findBy(['domain' => $domain]); $questions = []; foreach ($domain->getQuestions() as $n => $question) { /** @var Model\Question $question */ $key = array_search($question, $toRemoveQuestions); if (false !== $key) { unset($toRemoveQuestions[$key]); } // $question->setPosition($n); $question->setDomain($domain); // get all existing Answers $toRemoveAnswers = $this->entityManager->getRepository(Model\Answer::class)->findBy(['question' => $question]); $answers = []; foreach ($question->getAnswers() as $l => $answer) { /** @var Model\Answer $answer */ $key = array_search($answer, $toRemoveAnswers); if (false !== $key) { unset($toRemoveAnswers[$key]); } // $answer->setPosition($l); $answer->setQuestion($question); $answers[] = $answer; } foreach ($toRemoveAnswers as $removing) { $this->entityManager->remove($removing); } $question->setAnswers($answers); $questions[] = $question; } foreach ($toRemoveQuestions as $removing) { $this->entityManager->remove($removing); } $domain->setQuestions($questions); $domain->setReferentiel($object); $domains[] = $domain; } // Remove deleted domains foreach ($toRemove as $removing) { $this->entityManager->remove($removing); } $object->setDomains($domains); } /** * The list action view * Get data & display them. */ public function listAction(): Response { return $this->render('Maturity/Referentiel/list.html.twig', [ 'totalItem' => $this->repository->count(), 'route' => $this->router->generate('maturity_referentiel_list_datatables'), ]); } public function listDataTables(Request $request): JsonResponse { $referentiels = $this->getResults($request); $reponse = $this->getBaseDataTablesResponse($request, $referentiels); foreach ($referentiels as $referentiel) { $reponse['data'][] = [ 'name' => $referentiel->getName(), 'description' => $referentiel->getDescription(), 'createdAt' => date_format($referentiel->getCreatedAt(), 'd-m-Y H:i'), 'updatedAt' => date_format($referentiel->getUpdatedAt(), 'd-m-Y H:i'), 'actions' => $this->generateActionCellContent($referentiel), ]; } $reponse['recordsTotal'] = count($reponse['data']); $reponse['recordsFiltered'] = count($reponse['data']); return new JsonResponse($reponse); } private function generateActionCellContent(Model\Referentiel $referentiel) { $id = $referentiel->getId(); $htmltoReturnIfAdmin = ''; if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) { $htmltoReturnIfAdmin = '<a href="' . $this->router->generate('maturity_referentiel_rights', ['id' => $id]) . '"> <i class="fa fa-user-shield"></i>' . $this->translator->trans('action.rights') . '</a>'; } return '<a href="' . $this->router->generate('maturity_referentiel_edit', ['id' => $id]) . '"> <i class="fa fa-pencil-alt"></i>' . $this->translator->trans('action.edit') . '</a>' . $htmltoReturnIfAdmin . '<a href="' . $this->router->generate('maturity_referentiel_duplicate', ['id' => $id]) . '"> <i class="fa fa-clone"></i>' . $this->translator->trans('action.duplicate') . '</a>' . '<a href="' . $this->router->generate('maturity_referentiel_export', ['id' => $id]) . '"> <i class="fa fa-file-code"></i>' . $this->translator->trans('action.export') . '</a>' . '<a href="' . $this->router->generate('maturity_referentiel_delete', ['id' => $id]) . '"> <i class="fa fa-trash"></i>' . $this->translator->trans('action.delete') . '</a>'; } protected function getLabelAndKeysArray(): array { return [ '0' => 'name', '1' => 'description', '2' => 'createdAt', '3' => 'updatedAt', '4' => 'actions', ]; } public function rightsAction(Request $request, string $id): Response { $object = $this->repository->findOneById($id); if (!$object) { throw new NotFoundHttpException("No object found with ID '{$id}'"); } $form = $this->createForm(ModeleReferentielRightsType::class, $object); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $this->formPrePersistData($object); $this->entityManager->persist($object); $this->entityManager->flush(); $this->addFlash('success', $this->getFlashbagMessage('success', 'rights', $object)); return $this->redirectToRoute($this->getRouteName('list')); } return $this->render('Maturity/Referentiel/rights.html.twig', [ 'form' => $form->createView(), ]); } public function exportAction(string $id) { $object = $this->repository->findOneById($id); if (!$object) { throw new NotFoundHttpException("No object found with ID '{$id}'"); } /** @var Model\Referentiel $toExport */ $toExport = clone $object; $serializer = SerializerBuilder::create()->build(); $xml = $serializer->serialize($toExport, 'xml'); $response = new Response($xml); $disposition = $response->headers->makeDisposition( ResponseHeaderBag::DISPOSITION_ATTACHMENT, self::formatToFileCompliant($object->getName()) . '.xml' ); $response->headers->set('Content-Disposition', $disposition); return $response; } public function importAction(Request $request) { $form = $this->createForm(ImportModeleType::class); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $content = file_get_contents($form->getData()['file']->getPathname()); $serializer = SerializerBuilder::create()->build(); /* @var Model\Referentiel $object */ try { $object = $serializer->deserialize($content, Model\Referentiel::class, 'xml'); $object->deserialize(); } catch (\Exception $e) { $this->addFlash('danger', "Impossible d'importer ce fichier"); return $this->redirectToRoute($this->getRouteName('list')); } // $object->setDomains($domain); $object->setCreatedAt(new \DateTimeImmutable()); $object->setName('(import) ' . $object->getName()); $this->entityManager->persist($object); $this->entityManager->flush(); $this->addFlash('success', $this->getFlashbagMessage('success', 'import', $object)); return $this->redirectToRoute($this->getRouteName('list')); } return $this->render($this->getTemplatingBasePath('import'), [ 'form' => $form->createView(), ]); } private static function formatToFileCompliant(string $string) { $unwanted_array = [ 'Š' => 'S', 'š' => 's', 'Ž' => 'Z', 'ž' => 'z', 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'A', 'Ç' => 'C', 'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O', 'Ø' => 'O', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ý' => 'Y', 'Þ' => 'B', 'ß' => 'Ss', 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'a', 'ç' => 'c', 'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ð' => 'o', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ý' => 'y', 'þ' => 'b', 'ÿ' => 'y', ]; return strtr($string, $unwanted_array); } }