Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 219 |
|
0.00% |
0 / 20 |
CRAP | |
0.00% |
0 / 1 |
ModeleAnalyseController | |
0.00% |
0 / 219 |
|
0.00% |
0 / 20 |
3422 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
getDomain | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getModel | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getModelClass | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getFormType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
listAction | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
formPrePersistData | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
72 | |||
createAction | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
12 | |||
editAction | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
20 | |||
getQuestionsConformite | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
duplicateAction | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
rightsAction | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
20 | |||
listDataTables | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
72 | |||
generateActioNCellContent | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
6 | |||
getLabelAndKeysArray | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
exportAction | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
6 | |||
importAction | |
0.00% |
0 / 33 |
|
0.00% |
0 / 1 |
56 | |||
formatToFileCompliant | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
deleteConfirmationAction | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
30 | |||
ScenarioMenacesToDelete | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
20 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace App\Domain\AIPD\Controller; |
6 | |
7 | use App\Application\Controller\CRUDController; |
8 | use App\Application\Symfony\Security\UserProvider; |
9 | use App\Application\Traits\ServersideDatatablesTrait; |
10 | use App\Domain\AIPD\Dictionary\BaseCriterePrincipeFondamental; |
11 | use App\Domain\AIPD\Form\Flow\ModeleAIPDFlow; |
12 | use App\Domain\AIPD\Form\Type\ImportModeleType; |
13 | use App\Domain\AIPD\Form\Type\ModeleAnalyseRightsType; |
14 | use App\Domain\AIPD\Form\Type\ModeleAnalyseType; |
15 | use App\Domain\AIPD\Model\ModeleAnalyse; |
16 | use App\Domain\AIPD\Model\ModeleQuestionConformite; |
17 | use App\Domain\AIPD\Model\ModeleScenarioMenace; |
18 | use App\Domain\AIPD\Repository; |
19 | use App\Domain\Registry\Repository\ConformiteTraitement\Question; |
20 | use App\Domain\User\Repository\Collectivity; |
21 | use App\Infrastructure\ORM\AIPD\Repository\ModeleMesureProtection as ModeleMesureProtectionRepository; |
22 | use Doctrine\ORM\EntityManagerInterface; |
23 | use Gaufrette\Exception\FileNotFound; |
24 | use Gaufrette\FilesystemInterface; |
25 | use JMS\Serializer\SerializerBuilder; |
26 | use Knp\Snappy\Pdf; |
27 | use Ramsey\Uuid\Uuid; |
28 | use Symfony\Component\HttpFoundation\JsonResponse; |
29 | use Symfony\Component\HttpFoundation\Request; |
30 | use Symfony\Component\HttpFoundation\Response; |
31 | use Symfony\Component\HttpFoundation\ResponseHeaderBag; |
32 | use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; |
33 | use Symfony\Component\Routing\RouterInterface; |
34 | use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; |
35 | use Symfony\Contracts\Translation\TranslatorInterface; |
36 | use Symfony\Polyfill\Intl\Icu\Exception\MethodNotImplementedException; |
37 | use Symfony\Polyfill\Intl\Icu\Exception\NotImplementedException; |
38 | |
39 | /** |
40 | * @property Repository\ModeleAnalyse $repository |
41 | */ |
42 | class ModeleAnalyseController extends CRUDController |
43 | { |
44 | use ServersideDatatablesTrait; |
45 | |
46 | /** |
47 | * @var Collectivity |
48 | */ |
49 | protected $collectivityRepository; |
50 | |
51 | private ModeleAIPDFlow $modeleFlow; |
52 | private Question $questionRepository; |
53 | private ModeleMesureProtectionRepository $mesureProtectionRepository; |
54 | private RouterInterface $router; |
55 | private FilesystemInterface $fichierFilesystem; |
56 | |
57 | public function __construct( |
58 | EntityManagerInterface $entityManager, |
59 | TranslatorInterface $translator, |
60 | Repository\ModeleAnalyse $repository, |
61 | Pdf $pdf, |
62 | UserProvider $userProvider, |
63 | AuthorizationCheckerInterface $authorizationChecker, |
64 | Collectivity $collectivityRepository, |
65 | ModeleMesureProtectionRepository $mesureProtectionRepository, |
66 | ModeleAIPDFlow $modeleFlow, |
67 | Question $questionRepository, |
68 | RouterInterface $router, |
69 | FilesystemInterface $fichierFilesystem, |
70 | ) { |
71 | parent::__construct($entityManager, $translator, $repository, $pdf, $userProvider, $authorizationChecker); |
72 | $this->collectivityRepository = $collectivityRepository; |
73 | $this->modeleFlow = $modeleFlow; |
74 | $this->questionRepository = $questionRepository; |
75 | $this->mesureProtectionRepository = $mesureProtectionRepository; |
76 | $this->router = $router; |
77 | $this->fichierFilesystem = $fichierFilesystem; |
78 | } |
79 | |
80 | protected function getDomain(): string |
81 | { |
82 | return 'aipd'; |
83 | } |
84 | |
85 | protected function getModel(): string |
86 | { |
87 | return 'modele_analyse'; |
88 | } |
89 | |
90 | protected function getModelClass(): string |
91 | { |
92 | return ModeleAnalyse::class; |
93 | } |
94 | |
95 | protected function getFormType(): string |
96 | { |
97 | return ModeleAnalyseType::class; |
98 | } |
99 | |
100 | public function listAction(): Response |
101 | { |
102 | return $this->render('Aipd/Modele_analyse/list.html.twig', [ |
103 | 'totalItem' => $this->repository->count(), |
104 | 'route' => $this->router->generate('aipd_modele_analyse_list_datatables'), |
105 | ]); |
106 | } |
107 | |
108 | /** |
109 | * {@inheritdoc} |
110 | * - Upload documentFile before object persistence in database. |
111 | * |
112 | * @throws \Exception |
113 | */ |
114 | public function formPrePersistData($object, $form = null) |
115 | { |
116 | if (!$object instanceof ModeleAnalyse) { |
117 | throw new \RuntimeException('You must persist a ' . ModeleAnalyse::class . ' object class with your form'); |
118 | } |
119 | |
120 | foreach ($object->getCriterePrincipeFondamentaux() as $criterePrincipeFondamental) { |
121 | $deleteFile = $criterePrincipeFondamental->isDeleteFile(); |
122 | |
123 | if ($deleteFile) { |
124 | // Remove existing file |
125 | try { |
126 | $this->fichierFilesystem->delete($criterePrincipeFondamental->getFichier()); |
127 | } catch (FileNotFound $e) { |
128 | } |
129 | |
130 | $criterePrincipeFondamental->setFichier(null); |
131 | } |
132 | |
133 | $file = $criterePrincipeFondamental->getFichierFile(); |
134 | |
135 | if ($file) { |
136 | if (null !== $existing = $criterePrincipeFondamental->getFichier()) { |
137 | try { |
138 | $this->fichierFilesystem->delete($existing); |
139 | } catch (FileNotFound $e) { |
140 | } |
141 | } |
142 | $filename = Uuid::uuid4()->toString() . '.' . $file->getClientOriginalExtension(); |
143 | $this->fichierFilesystem->write($filename, \fopen($file->getRealPath(), 'r')); |
144 | $criterePrincipeFondamental->setFichier($filename); |
145 | $criterePrincipeFondamental->setFichierFile(null); |
146 | } |
147 | } |
148 | } |
149 | |
150 | public function createAction(Request $request): Response |
151 | { |
152 | $object = new ModeleAnalyse(); |
153 | $object->setCriterePrincipeFondamentaux(BaseCriterePrincipeFondamental::getBaseCritere()); |
154 | $object->setQuestionConformites($this->getQuestionsConformite($object)); |
155 | |
156 | $this->modeleFlow->bind($object); |
157 | $form = $this->modeleFlow->createForm(); |
158 | |
159 | if ($this->modeleFlow->isValid($form)) { |
160 | $this->formPrePersistData($object); |
161 | $this->modeleFlow->saveCurrentStepData($form); |
162 | |
163 | if ($this->modeleFlow->nextStep()) { |
164 | $form = $this->modeleFlow->createForm(); |
165 | } else { |
166 | $this->entityManager->persist($object); |
167 | $this->entityManager->flush(); |
168 | |
169 | $this->modeleFlow->reset(); |
170 | |
171 | $this->addFlash('success', $this->getFlashbagMessage('success', 'create', $object)); |
172 | |
173 | return $this->redirectToRoute($this->getRouteName('list')); |
174 | } |
175 | } |
176 | |
177 | return $this->render($this->getTemplatingBasePath('create'), [ |
178 | 'form' => $form->createView(), |
179 | 'flow' => $this->modeleFlow, |
180 | ]); |
181 | } |
182 | |
183 | public function editAction(Request $request, string $id): Response |
184 | { |
185 | $object = $this->repository->findOneById($id); |
186 | if (!$object) { |
187 | throw new NotFoundHttpException("No object found with ID '{$id}'"); |
188 | } |
189 | |
190 | $this->modeleFlow->bind($object); |
191 | $form = $this->modeleFlow->createForm(); |
192 | |
193 | if ($this->modeleFlow->isValid($form)) { |
194 | $this->formPrePersistData($object); |
195 | $this->modeleFlow->saveCurrentStepData($form); |
196 | |
197 | if ($this->modeleFlow->nextStep()) { |
198 | $form = $this->modeleFlow->createForm(); |
199 | } else { |
200 | $this->ScenarioMenacesToDelete($object, $id); |
201 | $this->entityManager->persist($object); |
202 | $this->entityManager->flush(); |
203 | |
204 | $this->modeleFlow->reset(); |
205 | |
206 | $this->addFlash('success', $this->getFlashbagMessage('success', 'edit', $object)); |
207 | |
208 | return $this->redirectToRoute($this->getRouteName('list')); |
209 | } |
210 | } |
211 | |
212 | return $this->render($this->getTemplatingBasePath('edit'), [ |
213 | 'form' => $form->createView(), |
214 | 'flow' => $this->modeleFlow, |
215 | ]); |
216 | } |
217 | |
218 | private function getQuestionsConformite(ModeleAnalyse $modeleAnalyse) |
219 | { |
220 | $questions = []; |
221 | foreach ($this->questionRepository->findAll(['position' => 'ASC']) as $question) { |
222 | $questions[] = new ModeleQuestionConformite($question->getQuestion(), $question->getPosition(), $modeleAnalyse); |
223 | } |
224 | |
225 | return $questions; |
226 | } |
227 | |
228 | public function duplicateAction(string $id): Response |
229 | { |
230 | throw new NotImplementedException('Not implemented yet'); |
231 | } |
232 | |
233 | public function rightsAction(Request $request, string $id): Response |
234 | { |
235 | $object = $this->repository->findOneById($id); |
236 | if (!$object) { |
237 | throw new NotFoundHttpException("No object found with ID '{$id}'"); |
238 | } |
239 | $form = $this->createForm(ModeleAnalyseRightsType::class, $object); |
240 | |
241 | $form->handleRequest($request); |
242 | |
243 | if ($form->isSubmitted() && $form->isValid()) { |
244 | $this->formPrePersistData($object); |
245 | $this->entityManager->persist($object); |
246 | $this->entityManager->flush(); |
247 | |
248 | $this->addFlash('success', $this->getFlashbagMessage('success', 'rights', $object)); |
249 | |
250 | return $this->redirectToRoute($this->getRouteName('list')); |
251 | } |
252 | |
253 | return $this->render('Aipd/Modele_analyse/rights.html.twig', [ |
254 | 'form' => $form->createView(), |
255 | ]); |
256 | } |
257 | |
258 | public function listDataTables(Request $request): JsonResponse |
259 | { |
260 | $modeles = $this->getResults($request); |
261 | $reponse = $this->getBaseDataTablesResponse($request, $modeles); |
262 | |
263 | foreach ($modeles as $modele) { |
264 | if (!$this->authorizationChecker->isGranted('ROLE_ADMIN')) { |
265 | $userCollectivity = $this->userProvider->getAuthenticatedUser()->getCollectivity(); |
266 | $userCollectivityType = $userCollectivity->getType(); |
267 | $authorizedCollectivities = $modele->getAuthorizedCollectivities(); |
268 | $authorizedCollectivityTypes = $modele->getAuthorizedCollectivityTypes(); |
269 | |
270 | if (!\is_null($authorizedCollectivityTypes) |
271 | && in_array($userCollectivityType, $authorizedCollectivityTypes)) { |
272 | continue; |
273 | } |
274 | |
275 | if ($authorizedCollectivities->contains($userCollectivity)) { |
276 | continue; |
277 | } |
278 | } |
279 | |
280 | $reponse['data'][] = [ |
281 | 'nom' => $modele->getNom(), |
282 | 'description' => $modele->getDescription(), |
283 | 'createdAt' => $modele->getCreatedAt() ? $modele->getCreatedAt()->format('d-m-Y H:i') : '', |
284 | 'updatedAt' => $modele->getUpdatedAt() ? $modele->getUpdatedAt()->format('d-m-Y H:i') : '', |
285 | 'actions' => $this->generateActioNCellContent($modele), |
286 | ]; |
287 | } |
288 | |
289 | $reponse['recordsTotal'] = count($reponse['data']); |
290 | $reponse['recordsFiltered'] = count($reponse['data']); |
291 | |
292 | $jsonResponse = new JsonResponse($reponse); |
293 | |
294 | return $jsonResponse; |
295 | } |
296 | |
297 | private function generateActioNCellContent(ModeleAnalyse $modele) |
298 | { |
299 | $id = $modele->getId(); |
300 | $htmltoReturnIfAdmin = ''; |
301 | |
302 | if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) { |
303 | $htmltoReturnIfAdmin = '<a href="' . $this->router->generate('aipd_modele_analyse_rights', ['id' => $id]) . '"> |
304 | <i aria-hidden="true" class="fa fa-user-shield"></i> ' |
305 | . $this->translator->trans('global.action.rights') . |
306 | '</a>'; |
307 | } |
308 | |
309 | return |
310 | '<a href="' . $this->router->generate('aipd_modele_analyse_edit', ['id' => $id]) . '"> |
311 | <i aria-hidden="true" class="fa fa-pencil"></i> ' |
312 | . $this->translator->trans('global.action.edit') . |
313 | '</a>' |
314 | . $htmltoReturnIfAdmin . |
315 | '<a href="' . $this->router->generate('aipd_modele_analyse_export', ['id' => $id]) . '"> |
316 | <i aria-hidden="true" class="fa fa-file-code"></i> ' . |
317 | $this->translator->trans('global.action.export') . |
318 | '</a>' . |
319 | '<a href="' . $this->router->generate('aipd_modele_analyse_delete', ['id' => $id]) . '"> |
320 | <i aria-hidden="true" class="fa fa-trash"></i> ' . |
321 | $this->translator->trans('global.action.delete') . |
322 | '</a>'; |
323 | } |
324 | |
325 | protected function getLabelAndKeysArray(): array |
326 | { |
327 | return [ |
328 | '0' => 'nom', |
329 | '1' => 'description', |
330 | '2' => 'createdAt', |
331 | '3' => 'updatedAt', |
332 | '4' => 'actions', |
333 | ]; |
334 | } |
335 | |
336 | public function exportAction(string $id) |
337 | { |
338 | $object = $this->repository->findOneById($id); |
339 | if (!$object) { |
340 | throw new NotFoundHttpException("No object found with ID '{$id}'"); |
341 | } |
342 | /** @var ModeleAnalyse $toExport */ |
343 | $toExport = clone $object; |
344 | $toExport->setCriterePrincipeFondamentaux($toExport->getCriterePrincipeFondamentaux()->toArray()); |
345 | |
346 | $serializer = SerializerBuilder::create()->build(); |
347 | $xml = $serializer->serialize($toExport, 'xml'); |
348 | |
349 | $response = new Response($xml); |
350 | $disposition = $response->headers->makeDisposition( |
351 | ResponseHeaderBag::DISPOSITION_ATTACHMENT, |
352 | self::formatToFileCompliant($object->getNom()) . '.xml' |
353 | ); |
354 | |
355 | $response->headers->set('Content-Disposition', $disposition); |
356 | |
357 | return $response; |
358 | } |
359 | |
360 | public function importAction(Request $request) |
361 | { |
362 | $form = $this->createForm(ImportModeleType::class); |
363 | $form->handleRequest($request); |
364 | if ($form->isSubmitted() && $form->isValid()) { |
365 | $content = file_get_contents($form->getData()['file']->getPathname()); |
366 | try { |
367 | $serializer = SerializerBuilder::create()->build(); |
368 | |
369 | // Replace all invalid dates with actual date |
370 | // Fixes https://gitlab.adullact.net/soluris/madis/-/issues/882 |
371 | $content = str_replace('<created_at><![CDATA[-0001', '<created_at><![CDATA[' . date('Y'), $content); |
372 | $content = str_replace('<updated_at><![CDATA[-0001', '<updated_at><![CDATA[' . date('Y'), $content); |
373 | /** @var ModeleAnalyse $object */ |
374 | $object = $serializer->deserialize($content, ModeleAnalyse::class, 'xml'); |
375 | $object->deserialize(); |
376 | } catch (\Exception $e) { |
377 | $this->addFlash('danger', "Impossible d'importer ce fichier : " . $e->getMessage()); |
378 | |
379 | return $this->redirectToRoute($this->getRouteName('list')); |
380 | } |
381 | |
382 | $sm = []; |
383 | foreach ($object->getScenarioMenaces() as $scenarioMenace) { |
384 | /** @var ModeleScenarioMenace $scenarioMenace */ |
385 | $mesures = []; |
386 | foreach ($scenarioMenace->getMesuresProtections() as $mesureProtection) { |
387 | // Check if this mesure already exists |
388 | $mm = $this->entityManager->find(\App\Domain\AIPD\Model\ModeleMesureProtection::class, $mesureProtection->getId()); |
389 | if ($mm) { |
390 | $mesures[] = $mm; |
391 | } else { |
392 | // If not, save it now |
393 | $this->entityManager->persist($mesureProtection); |
394 | $mesures[] = $mesureProtection; |
395 | } |
396 | } |
397 | $scenarioMenace->setMesuresProtections($mesures); |
398 | $sm[] = $scenarioMenace; |
399 | } |
400 | |
401 | $object->setScenarioMenaces($sm); |
402 | $object->setCreatedAt(new \DateTimeImmutable()); |
403 | $object->setNom('(import) ' . $object->getNom()); |
404 | $this->entityManager->persist($object); |
405 | $this->entityManager->flush(); |
406 | $this->addFlash('success', $this->getFlashbagMessage('success', 'import', $object)); |
407 | |
408 | return $this->redirectToRoute($this->getRouteName('list')); |
409 | } |
410 | |
411 | return $this->render($this->getTemplatingBasePath('import'), [ |
412 | 'form' => $form->createView(), |
413 | ]); |
414 | } |
415 | |
416 | private static function formatToFileCompliant(string $string) |
417 | { |
418 | $unwanted_array = [ |
419 | 'Š' => 'S', 'š' => 's', 'Ž' => 'Z', 'ž' => 'z', 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'A', 'Ç' => 'C', 'È' => 'E', 'É' => 'E', |
420 | 'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O', 'Ø' => 'O', 'Ù' => 'U', |
421 | 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ý' => 'Y', 'Þ' => 'B', 'ß' => 'Ss', 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'a', 'ç' => 'c', |
422 | 'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ð' => 'o', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', |
423 | 'ö' => 'o', 'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ý' => 'y', 'þ' => 'b', 'ÿ' => 'y', |
424 | ]; |
425 | |
426 | return strtr($string, $unwanted_array); |
427 | } |
428 | |
429 | /** |
430 | * The deletion action |
431 | * Delete the data. |
432 | * |
433 | * @throws \Exception |
434 | */ |
435 | public function deleteConfirmationAction(string $id): Response |
436 | { |
437 | $object = $this->repository->findOneById($id); |
438 | if (!$object) { |
439 | throw new NotFoundHttpException("No object found with ID '{$id}'"); |
440 | } |
441 | |
442 | if ($this->isSoftDelete()) { |
443 | if (!\method_exists($object, 'setDeletedAt')) { |
444 | throw new MethodNotImplementedException('setDeletedAt'); |
445 | } |
446 | $object->setDeletedAt(new \DateTimeImmutable()); |
447 | $this->repository->update($object); |
448 | } else { |
449 | foreach ($this->mesureProtectionRepository->findToDelete($object) as $measureToDelete) { |
450 | $this->entityManager->remove($measureToDelete); |
451 | } |
452 | $this->entityManager->remove($object); |
453 | |
454 | $this->entityManager->flush(); |
455 | } |
456 | |
457 | $this->addFlash('success', $this->getFlashbagMessage('success', 'delete', $object)); |
458 | |
459 | return $this->redirectToRoute($this->getRouteName('list')); |
460 | } |
461 | |
462 | private function ScenarioMenacesToDelete($object, $modeleAnalyseId) |
463 | { |
464 | $ScenarioMenacesToDelete = []; |
465 | $scenarioMenacesActual = $this->entityManager->getRepository(ModeleScenarioMenace::class)->findBy(['modeleAnalyse' => $modeleAnalyseId]); |
466 | |
467 | foreach ($scenarioMenacesActual as $actualScenarioMenace) { |
468 | if (!in_array($actualScenarioMenace, $object->getScenarioMenaces()->toArray())) { |
469 | $ScenarioMenacesToDelete[] = $actualScenarioMenace; |
470 | } |
471 | } |
472 | |
473 | foreach ($ScenarioMenacesToDelete as $menaceToDelete) { |
474 | /** @var ModeleScenarioMenace $menace */ |
475 | $menace = $this->entityManager->getRepository(ModeleScenarioMenace::class)->find($menaceToDelete->getId()); |
476 | $this->entityManager->remove((object) $menace); |
477 | } |
478 | } |
479 | } |