Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
6.88% covered (danger)
6.88%
15 / 218
35.29% covered (danger)
35.29%
6 / 17
CRAP
0.00% covered (danger)
0.00%
0 / 1
ReferentielController
6.88% covered (danger)
6.88%
15 / 218
35.29% covered (danger)
35.29%
6 / 17
1680.10
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getDomain
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getModel
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getModelClass
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFormType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createAction
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 editAction
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
 duplicateAction
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
42
 formPrePersistData
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
110
 listAction
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 listDataTables
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
 generateActionCellContent
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
6
 getLabelAndKeysArray
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 rightsAction
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 exportAction
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
 importAction
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
20
 formatToFileCompliant
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4 * This file is part of the MADIS - RGPD Management application.
5 *
6 * @copyright Copyright (c) 2018-2019 Soluris - Solutions Numériques Territoriales Innovantes
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22declare(strict_types=1);
23
24namespace App\Domain\Maturity\Controller;
25
26use App\Application\Controller\CRUDController;
27use App\Application\Symfony\Security\UserProvider;
28use App\Application\Traits\ServersideDatatablesTrait;
29use App\Domain\Maturity\Form\Type\ImportModeleType;
30use App\Domain\Maturity\Form\Type\ModeleReferentielRightsType;
31use App\Domain\Maturity\Form\Type\ReferentielType;
32use App\Domain\Maturity\Model;
33use App\Domain\Maturity\Repository;
34use Doctrine\ORM\EntityManagerInterface;
35use JMS\Serializer\SerializerBuilder;
36use Knp\Snappy\Pdf;
37use Symfony\Component\HttpFoundation\JsonResponse;
38use Symfony\Component\HttpFoundation\Request;
39use Symfony\Component\HttpFoundation\Response;
40use Symfony\Component\HttpFoundation\ResponseHeaderBag;
41use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
42use Symfony\Component\Routing\RouterInterface;
43use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
44use Symfony\Contracts\Translation\TranslatorInterface;
45
46/**
47 * @property Repository\Referentiel $repository
48 */
49class ReferentielController extends CRUDController
50{
51    use ServersideDatatablesTrait;
52
53    /**
54     * @var AuthorizationCheckerInterface
55     */
56    protected $authorizationChecker;
57
58    /**
59     * @var UserProvider
60     */
61    protected $userProvider;
62
63    protected RouterInterface $router;
64
65    public function __construct(
66        EntityManagerInterface $entityManager,
67        TranslatorInterface $translator,
68        Repository\Referentiel $repository,
69        AuthorizationCheckerInterface $authorizationChecker,
70        UserProvider $userProvider,
71        RouterInterface $router,
72        Pdf $pdf,
73    ) {
74        parent::__construct($entityManager, $translator, $repository, $pdf, $userProvider, $authorizationChecker);
75        $this->authorizationChecker = $authorizationChecker;
76        $this->userProvider         = $userProvider;
77        $this->router               = $router;
78    }
79
80    protected function getDomain(): string
81    {
82        return 'maturity';
83    }
84
85    protected function getModel(): string
86    {
87        return 'referentiel';
88    }
89
90    protected function getModelClass(): string
91    {
92        return Model\Referentiel::class;
93    }
94
95    protected function getFormType(): string
96    {
97        return ReferentielType::class;
98    }
99
100    /**
101     * {@inheritdoc}
102     * Override method in order to hydrate survey answers.
103     */
104    public function createAction(Request $request): Response
105    {
106        /**
107         * @var Model\Referentiel
108         */
109        $modelClass = $this->getModelClass();
110        $object     = new $modelClass();
111
112        $form = $this->createForm($this->getFormType(), $object);
113
114        $form->handleRequest($request);
115        if ($form->isSubmitted() && $form->isValid()) {
116            $this->formPrePersistData($object);
117            $this->entityManager->persist($object);
118            $this->entityManager->flush();
119
120            $this->addFlash('success', $this->getFlashbagMessage('success', 'create', $object->getName()));
121
122            return $this->redirectToRoute($this->getRouteName('list'));
123        }
124
125        return $this->render($this->getTemplatingBasePath('create'), [
126            'form' => $form->createView(),
127        ]);
128    }
129
130    public function editAction(Request $request, string $id): Response
131    {
132        /** @var Model\Referentiel $object */
133        $object = $this->repository->findOneById($id);
134        if (!$object) {
135            throw new NotFoundHttpException("No object found with ID '{$id}'");
136        }
137
138        $sections = $this->entityManager->getRepository(Model\Domain::class)->findBy(['referentiel' => $object]);
139
140        $form = $this->createForm($this->getFormType(), $object, ['validation_groups' => ['default', $this->getModel(), 'edit']]);
141
142        $form->handleRequest($request);
143        if ($form->isSubmitted() && $form->isValid()) {
144            $this->formPrePersistData($object);
145            $this->entityManager->persist($object);
146            $this->entityManager->flush();
147
148            $this->addFlash('success', $this->getFlashbagMessage('success', 'edit', $object));
149
150            return $this->redirectToRoute($this->getRouteName('list'));
151        }
152
153        return $this->render($this->getTemplatingBasePath('edit'), [
154            'form' => $form->createView(),
155        ]);
156    }
157
158    public function duplicateAction(Request $request, string $id): Response
159    {
160        /** @var Model\Referentiel $object */
161        $object = $this->repository->findOneById($id);
162        if (!$object) {
163            throw new NotFoundHttpException("No object found with ID '{$id}'");
164        }
165
166        $newRef = new Model\Referentiel();
167
168        $newRef->setName($object->getName());
169        $newRef->setDescription($object->getDescription());
170
171        $newRef->setAuthorizedCollectivities($object->getAuthorizedCollectivities());
172
173        $newRef->setAuthorizedCollectivityTypes($object->getAuthorizedCollectivityTypes());
174        $newRef->setOptionRightSelection($newRef->getOptionRightSelection());
175
176        /** @var Model\Domain $domain */
177        foreach ($object->getDomains() as $domain) {
178            $d = new Model\Domain();
179            $d->setName($domain->getName());
180            $d->setDescription($domain->getDescription());
181            $d->setReferentiel($newRef);
182            $d->setColor($domain->getColor());
183            $d->setPosition($domain->getPosition());
184
185            $this->entityManager->persist($d);
186            /** @var Model\Question $q */
187            foreach ($domain->getQuestions() as $q) {
188                $newQ = new Model\Question();
189                $newQ->setPosition($q->getPosition());
190                $newQ->setName($q->getName());
191                $newQ->setOptional($q->getOptional());
192                if ($q->getOptional()) {
193                    $newQ->setOptionReason($q->getOptionReason());
194                }
195                $newQ->setWeight($q->getWeight());
196                $newQ->setDomain($d);
197                $this->entityManager->persist($newQ);
198                /** @var Model\Answer $a */
199                foreach ($q->getAnswers() as $a) {
200                    $newA = new Model\Answer();
201                    $newA->setName($a->getName());
202                    $newA->setPosition($a->getPosition());
203                    $newA->setRecommendation($a->getRecommendation());
204                    $newA->setResponse($a->getResponse());
205                    $newA->setQuestion($newQ);
206                    $this->entityManager->persist($newA);
207                }
208            }
209        }
210
211        $this->entityManager->persist($newRef);
212        $this->entityManager->flush();
213
214        $this->addFlash('success', $this->getFlashbagMessage('success', 'duplicate', $object));
215
216        return $this->redirectToRoute($this->getRouteName('edit'), ['id' => $newRef->getId()->toString()]);
217    }
218
219    public function formPrePersistData($object, $form = null)
220    {
221        $domains = [];
222
223        $colors = [
224            'info',
225            'success',
226            'primary',
227            'warning',
228        ];
229
230        // get all existing domains
231        $toRemove = $this->entityManager->getRepository(Model\Domain::class)->findBy(['referentiel' => $object]);
232
233        foreach ($object->getDomains() as $k => $domain) {
234            $key = array_search($domain, $toRemove);
235            if (false !== $key) {
236                unset($toRemove[$key]);
237            }
238            /* @var Model\Domain $domain */
239            // $domain->setPosition($k);
240            $domain->setColor($colors[$k % 4]);
241
242            // get all existing questions
243            $toRemoveQuestions = $this->entityManager->getRepository(Model\Question::class)->findBy(['domain' => $domain]);
244
245            $questions = [];
246            foreach ($domain->getQuestions() as $n => $question) {
247                /** @var Model\Question $question */
248                $key = array_search($question, $toRemoveQuestions);
249                if (false !== $key) {
250                    unset($toRemoveQuestions[$key]);
251                }
252
253                // $question->setPosition($n);
254                $question->setDomain($domain);
255                // get all existing Answers
256                $toRemoveAnswers = $this->entityManager->getRepository(Model\Answer::class)->findBy(['question' => $question]);
257                $answers         = [];
258                foreach ($question->getAnswers() as $l => $answer) {
259                    /** @var Model\Answer $answer */
260                    $key = array_search($answer, $toRemoveAnswers);
261                    if (false !== $key) {
262                        unset($toRemoveAnswers[$key]);
263                    }
264                    // $answer->setPosition($l);
265                    $answer->setQuestion($question);
266                    $answers[] = $answer;
267                }
268
269                foreach ($toRemoveAnswers as $removing) {
270                    $this->entityManager->remove($removing);
271                }
272
273                $question->setAnswers($answers);
274
275                $questions[] = $question;
276            }
277            foreach ($toRemoveQuestions as $removing) {
278                $this->entityManager->remove($removing);
279            }
280            $domain->setQuestions($questions);
281            $domain->setReferentiel($object);
282
283            $domains[] = $domain;
284        }
285        // Remove deleted domains
286        foreach ($toRemove as $removing) {
287            $this->entityManager->remove($removing);
288        }
289
290        $object->setDomains($domains);
291    }
292
293    /**
294     * The list action view
295     * Get data & display them.
296     */
297    public function listAction(): Response
298    {
299        return $this->render('Maturity/Referentiel/list.html.twig', [
300            'totalItem' => $this->repository->count(),
301            'route'     => $this->router->generate('maturity_referentiel_list_datatables'),
302        ]);
303    }
304
305    public function listDataTables(Request $request): JsonResponse
306    {
307        $referentiels = $this->getResults($request);
308        $reponse      = $this->getBaseDataTablesResponse($request, $referentiels);
309
310        foreach ($referentiels as $referentiel) {
311            $reponse['data'][] = [
312                'name'        => $referentiel->getName(),
313                'description' => $referentiel->getDescription(),
314                'createdAt'   => date_format($referentiel->getCreatedAt(), 'd-m-Y H:i'),
315                'updatedAt'   => date_format($referentiel->getUpdatedAt(), 'd-m-Y H:i'),
316                'actions'     => $this->generateActionCellContent($referentiel),
317            ];
318        }
319
320        $reponse['recordsTotal']    = count($reponse['data']);
321        $reponse['recordsFiltered'] = count($reponse['data']);
322
323        return new JsonResponse($reponse);
324    }
325
326    private function generateActionCellContent(Model\Referentiel $referentiel)
327    {
328        $id                  = $referentiel->getId();
329        $htmltoReturnIfAdmin = '';
330
331        if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
332            $htmltoReturnIfAdmin = '<a href="' . $this->router->generate('maturity_referentiel_rights', ['id' => $id]) . '">
333                <i aria-hidden="true" class="fa fa-user-shield"></i> '
334                . $this->translator->trans('global.action.rights') .
335                '</a>';
336        }
337
338        return
339            '<a href="' . $this->router->generate('maturity_referentiel_edit', ['id' => $id]) . '">
340                <i aria-hidden="true" class="fa fa-pencil"></i> '
341            . $this->translator->trans('global.action.edit') .
342            '</a>'
343            . $htmltoReturnIfAdmin .
344            '<a href="' . $this->router->generate('maturity_referentiel_duplicate', ['id' => $id]) . '">
345                <i aria-hidden="true" class="fa fa-clone"></i> ' .
346            $this->translator->trans('global.action.duplicate') .
347            '</a>' .
348            '<a href="' . $this->router->generate('maturity_referentiel_export', ['id' => $id]) . '">
349                <i aria-hidden="true" class="fa fa-file-code"></i> ' .
350            $this->translator->trans('global.action.export') .
351            '</a>' .
352            '<a href="' . $this->router->generate('maturity_referentiel_delete', ['id' => $id]) . '">
353                <i aria-hidden="true" class="fa fa-trash"></i> ' .
354            $this->translator->trans('global.action.delete') .
355            '</a>';
356    }
357
358    protected function getLabelAndKeysArray(): array
359    {
360        return [
361            '0' => 'name',
362            '1' => 'description',
363            '2' => 'createdAt',
364            '3' => 'updatedAt',
365            '4' => 'actions',
366        ];
367    }
368
369    public function rightsAction(Request $request, string $id): Response
370    {
371        $object = $this->repository->findOneById($id);
372        if (!$object) {
373            throw new NotFoundHttpException("No object found with ID '{$id}'");
374        }
375        $form = $this->createForm(ModeleReferentielRightsType::class, $object);
376
377        $form->handleRequest($request);
378
379        if ($form->isSubmitted() && $form->isValid()) {
380            $this->formPrePersistData($object);
381            $this->entityManager->persist($object);
382            $this->entityManager->flush();
383
384            $this->addFlash('success', $this->getFlashbagMessage('success', 'rights', $object));
385
386            return $this->redirectToRoute($this->getRouteName('list'));
387        }
388
389        return $this->render('Maturity/Referentiel/rights.html.twig', [
390            'form' => $form->createView(),
391        ]);
392    }
393
394    public function exportAction(string $id)
395    {
396        $object = $this->repository->findOneById($id);
397        if (!$object) {
398            throw new NotFoundHttpException("No object found with ID '{$id}'");
399        }
400        /** @var Model\Referentiel $toExport */
401        $toExport = clone $object;
402
403        $serializer = SerializerBuilder::create()->build();
404        $xml        = $serializer->serialize($toExport, 'xml');
405
406        $response    = new Response($xml);
407        $disposition = $response->headers->makeDisposition(
408            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
409            self::formatToFileCompliant($object->getName()) . '.xml'
410        );
411
412        $response->headers->set('Content-Disposition', $disposition);
413
414        return $response;
415    }
416
417    public function importAction(Request $request)
418    {
419        $form = $this->createForm(ImportModeleType::class);
420        $form->handleRequest($request);
421        if ($form->isSubmitted() && $form->isValid()) {
422            $content    = file_get_contents($form->getData()['file']->getPathname());
423            $serializer = SerializerBuilder::create()->build();
424            /* @var Model\Referentiel $object */
425
426            try {
427                $object = $serializer->deserialize($content, Model\Referentiel::class, 'xml');
428                $object->deserialize();
429            } catch (\Exception $e) {
430                $this->addFlash('danger', "Impossible d'importer ce fichier");
431
432                return $this->redirectToRoute($this->getRouteName('list'));
433            }
434            // $object->setDomains($domain);
435
436            $object->setCreatedAt(new \DateTimeImmutable());
437            $object->setName('(import) ' . $object->getName());
438            $this->entityManager->persist($object);
439            $this->entityManager->flush();
440            $this->addFlash('success', $this->getFlashbagMessage('success', 'import', $object));
441
442            return $this->redirectToRoute($this->getRouteName('list'));
443        }
444
445        return $this->render($this->getTemplatingBasePath('import'), [
446            'form' => $form->createView(),
447        ]);
448    }
449
450    private static function formatToFileCompliant(string $string)
451    {
452        $unwanted_array = [
453            'Š' => 'S', 'š' => 's', 'Ž' => 'Z', 'ž' => 'z', 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'A', 'Ç' => 'C', 'È' => 'E', 'É' => 'E',
454            'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O', 'Ø' => 'O', 'Ù' => 'U',
455            'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ý' => 'Y', 'Þ' => 'B', 'ß' => 'Ss', 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'a', 'ç' => 'c',
456            'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ð' => 'o', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o',
457            'ö' => 'o', 'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ý' => 'y', 'þ' => 'b', 'ÿ' => 'y',
458        ];
459
460        return strtr($string, $unwanted_array);
461    }
462}