Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 225
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ViolationGenerator
0.00% covered (danger)
0.00%
0 / 225
0.00% covered (danger)
0.00%
0 / 5
1122
0.00% covered (danger)
0.00%
0 / 1
 addGlobalOverview
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 1
210
 addSyntheticView
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
12
 addDetailedView
0.00% covered (danger)
0.00%
0 / 108
0.00% covered (danger)
0.00%
0 / 1
30
 translateWithDictionary
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 AnnexeList
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
56
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\Reporting\Generator\Word;
25
26use App\Domain\Registry\Dictionary\ViolationCauseDictionary;
27use App\Domain\Registry\Dictionary\ViolationCommunicationDictionary;
28use App\Domain\Registry\Dictionary\ViolationConcernedDataDictionary;
29use App\Domain\Registry\Dictionary\ViolationConcernedPeopleDictionary;
30use App\Domain\Registry\Dictionary\ViolationGravityDictionary;
31use App\Domain\Registry\Dictionary\ViolationImpactDictionary;
32use App\Domain\Registry\Dictionary\ViolationNatureDictionary;
33use App\Domain\Registry\Dictionary\ViolationNotificationDictionary;
34use App\Domain\Registry\Dictionary\ViolationOriginDictionary;
35use App\Domain\Registry\Model\Violation;
36use PhpOffice\PhpWord\Element\Section;
37
38class ViolationGenerator extends AbstractGenerator implements ImpressionGeneratorInterface
39{
40    /**
41     * Global overview : Information to display for violation in overview report.
42     *
43     * @throws \Exception
44     */
45    public function addGlobalOverview(Section $section, array $data): void
46    {
47        if ($this->collectivity) {
48            $collectivity = $this->collectivity;
49        } else {
50            $collectivity = $this->userProvider->getAuthenticatedUser()->getCollectivity();
51        }
52
53        $nbTotal = \count($data);
54
55        foreach ($data as $violation) {
56            $cellDate = $this->getDate($violation->getDate(), 'd/m/Y');
57            if ($violation->isInProgress()) {
58                $cellDate .= ' (Toujours en cours)';
59            }
60
61            $violationNatures      = $violation->getViolationNatures();
62            $violationNaturesArray = [];
63            foreach ($violationNatures as $violationNature) {
64                $violationNaturesArray[] = ViolationNatureDictionary::getNatures()[$violationNature];
65            }
66
67            $tableData[] = [
68                $cellDate,
69                $violationNaturesArray,
70                ViolationCauseDictionary::getNatures()[$violation->getCause()],
71                ViolationGravityDictionary::getGravities()[$violation->getGravity()],
72                ViolationNotificationDictionary::getNotifications()[$violation->getNotification()],
73                ViolationCommunicationDictionary::getCommunications()[$violation->getCommunication()],
74            ];
75        }
76
77        $section->addTitle('Registre des violations de données', 2);
78
79        $section->addText('Un registre des violations de données à caractère personnel est disponible et tenu à jour.');
80
81        if (0 === $nbTotal) {
82            $section->addText('À ce jour, il n’y a pas eu de violation de données à caractère personnel.');
83        } else {
84            if (1 === $nbTotal) {
85                $section->addText("Il y a eu {$nbTotal} violation de données à caractère personnel.");
86            } else {
87                $section->addText("Il y a eu {$nbTotal} violations de données à caractère personnel.");
88            }
89            $violationTable = $section->addTable($this->tableStyle);
90            $violationTable->addRow(null, ['tblHeader' => true, 'cantsplit' => true]);
91            $ViolationNames = [
92                ['name' => 'Date', 'width' => 1000, 'merge' => 'restart'],
93                ['name' => 'Nature', 'width' => 1500, 'merge' => 'restart'],
94                ['name' => 'Cause', 'width' => 1500, 'merge' => 'restart'],
95                ['name' => 'Niveau de gravité', 'width' => 1500, 'merge' => 'restart'],
96                ['name' => 'Notification', 'width' => 3000, 'merge' => null],
97            ];
98            foreach ($ViolationNames as $item) {
99                $cell = $violationTable->addCell($item['width'], ['bgColor' => '3c8dbc', 'vMerge' => $item['merge']]);
100                if ('Notification' === $item['name']) {
101                    $cell->getStyle()->setGridSpan(2);
102                }
103                $cell->addText($item['name'], $this->textHeadStyle);
104            }
105            $violationTable->addRow(null, ['tblHeader' => true, 'cantsplit' => true]);
106            $violationTable->addCell(1000, ['vMerge' => 'continue']);
107            $violationTable->addCell(1500, ['vMerge' => 'continue']);
108            $violationTable->addCell(1500, ['vMerge' => 'continue']);
109            $violationTable->addCell(1500, ['vMerge' => 'continue']);
110            $cell = $violationTable->addCell(1500, ['bgColor' => '3c8dbc', 'vAlign' => 'center']);
111            $cell->addText('CNIL', $this->textHeadStyle);
112            $cell = $violationTable->addCell(1500, ['bgColor' => '3c8dbc', 'vAlign' => 'center']);
113            $cell->addText('Concernés', $this->textHeadStyle);
114
115            foreach ($tableData as $key => $row) {
116                $violationTable->addRow(null, ['cantsplit' => true]);
117                foreach ($row as $cellItem) {
118                    $cell = $violationTable->addCell(0 === $key ? 1000 : 1500);
119                    if (is_array($cellItem)) {
120                        foreach ($cellItem as $item) {
121                            $cell->addListItem($item, (int) null, [], [], ['spaceAfter' => 0]);
122                        }
123                    } else {
124                        $cell->addText($cellItem);
125                    }
126                }
127            }
128        }
129    }
130
131    public function addSyntheticView(Section $section, array $data): void
132    {
133        $section->addTitle('Liste des violations', 1);
134
135        // Aggregate data before rendering
136        $tableData = [
137            [
138                'Date',
139                'Natures',
140                'Cause',
141                'Niveau de gravité',
142            ],
143        ];
144
145        foreach ($data as $violation) {
146            /** @var Violation $violation */
147            $cellDate = $this->getDate($violation->getDate(), 'd/m/Y');
148            if ($violation->isInProgress()) {
149                $cellDate .= ' (Toujours en cours)';
150            }
151
152            $natures     = join(', ', array_map(function ($n) { return ViolationNatureDictionary::getNatures()[$n] ?? $n; }, (array) $violation->getViolationNatures()));
153            $tableData[] = [
154                $cellDate,
155                $natures,
156                ViolationCauseDictionary::getNatures()[$violation->getCause()],
157                ViolationGravityDictionary::getGravities()[$violation->getGravity()],
158            ];
159        }
160
161        // Rendering
162        $this->addTable($section, $tableData, true, self::TABLE_ORIENTATION_HORIZONTAL);
163        $section->addPageBreak();
164    }
165
166    public function addDetailedView(Section $section, array $data): void
167    {
168        $section->addTitle('Détail des violations', 1);
169
170        foreach ($data as $key => $violation) {
171            if (0 !== $key) {
172                $section->addPageBreak();
173            }
174
175            $section->addTitle((string) $violation, 2);
176
177            $cellDate = $this->getDate($violation->getDate(), 'd/m/Y');
178
179            $generalInformationsData = [
180                [
181                    'Date de la violation',
182                    $cellDate,
183                ],
184            ];
185
186            // Ajouter les services si le module est actif
187            if ($violation->getCollectivity()->getIsServicesEnabled()) {
188                $generalInformationsData[] = [
189                    'Service',
190                    $violation->getService(),
191                ];
192            }
193
194            $natures                 = join(', ', array_map(function ($n) { return ViolationNatureDictionary::getNatures()[$n] ?? $n; }, (array) $violation->getViolationNatures()));
195            $generalInformationsData = array_merge($generalInformationsData, [
196                [
197                    'Violation en cours',
198                    $violation->isInProgress() ? 'Oui' : 'Non',
199                ],
200                [
201                    'Natures de la violation',
202                    $natures,
203                ],
204                [
205                    'Origine de la perte de données',
206                    $this->translateWithDictionary(ViolationOriginDictionary::getOrigins(), $violation->getOrigins()),
207                ],
208                [
209                    'Cause de la violation',
210                    $this->translateWithDictionary(ViolationCauseDictionary::getNatures(), $violation->getCause()),
211                ],
212                [
213                    'Nature des données concernées',
214                    $this->translateWithDictionary(ViolationConcernedDataDictionary::getConcernedData(), $violation->getConcernedDataNature()),
215                ],
216                [
217                    'Catégorie des personnes concernées',
218                    $this->translateWithDictionary(ViolationConcernedPeopleDictionary::getConcernedPeople(), $violation->getConcernedPeopleCategories()),
219                ],
220                [
221                    'Nombre approximatif d\'enregistrements concernés par la violation',
222                    $violation->getNbAffectedRows(),
223                ],
224                [
225                    'Nombre approximatif de personnes concernées par la violation',
226                    $violation->getNbAffectedPersons(),
227                ],
228            ]);
229
230            $consequenceData = [
231                [
232                    'Nature des impacts potentiels pour les personnes',
233                    $this->translateWithDictionary(ViolationImpactDictionary::getImpacts(), $violation->getPotentialImpactsNature()),
234                ],
235                [
236                    'Niveau de gravité',
237                    $this->translateWithDictionary(ViolationGravityDictionary::getGravities(), $violation->getGravity()),
238                ],
239                [
240                    'Communications aux personnes concernées',
241                    $this->translateWithDictionary(ViolationCommunicationDictionary::getCommunications(), $violation->getCommunication()),
242                ],
243                [
244                    'Précisions sur les communications',
245                    $violation->getCommunicationPrecision(),
246                ],
247                [
248                    'Mesures techniques et organisationnelles appliquées suite à la violation',
249                    $violation->getAppliedMeasuresAfterViolation(),
250                ],
251                [
252                    'Notification',
253                    $this->translateWithDictionary(ViolationNotificationDictionary::getNotifications(), $violation->getNotification()),
254                ],
255                [
256                    'Précisions sur les notifications',
257                    $violation->getNotificationDetails(),
258                ],
259                [
260                    'Commentaire',
261                    $violation->getComment(),
262                ],
263            ];
264
265            $historyData = [
266                [
267                    'Date de création',
268                    $this->getDate($violation->getCreatedAt()),
269                ],
270                [
271                    'Date de modification',
272                    $this->getDate($violation->getUpdatedAt()),
273                ],
274                [
275                    'Modifié par',
276                    $violation->getUpdatedBy(),
277                ],
278            ];
279
280            $section->addTitle('Informations sur la violation', 3);
281            $this->addTable($section, $generalInformationsData, false, self::TABLE_ORIENTATION_VERTICAL);
282
283            $section->addTitle('Conséquences de la violation', 3);
284            $this->addTable($section, $consequenceData, false, self::TABLE_ORIENTATION_VERTICAL);
285
286            $section->addTitle('Éléments associés', 3);
287            $this->addLinkedData($section, $violation);
288
289            $section->addTitle('Historique', 3);
290            $this->addTable($section, $historyData, false, self::TABLE_ORIENTATION_VERTICAL);
291        }
292    }
293
294    private function translateWithDictionary(array $dictionaryData = [], $value = null): array
295    {
296        if (\is_null($value)) {
297            return [];
298        }
299
300        if (!\is_array($value)) {
301            return [$dictionaryData[$value]];
302        }
303
304        // Value is iterable
305        $translatedValues = [];
306        foreach ($value as $item) {
307            $translatedValues[] = "{$dictionaryData[$item]}";
308        }
309
310        return $translatedValues;
311    }
312
313    public function AnnexeList(Section $section, $violations)
314    {
315        $section->addTitle('Liste des violations de données', 2);
316        $tableViolationAnnexeApplied = $section->addTable($this->tableStyle);
317        $tableViolationAnnexeApplied->addRow(null, ['tblHeader' => true, 'cantSplit' => true]);
318        $cell = $tableViolationAnnexeApplied->addCell(1000, $this->cellHeadStyle);
319        $cell->addText('Date', $this->textHeadStyle);
320        $cell = $tableViolationAnnexeApplied->addCell(1000, $this->cellHeadStyle);
321        $cell->addText('Communication aux personnes', $this->textHeadStyle);
322        $cell = $tableViolationAnnexeApplied->addCell(2000, $this->cellHeadStyle);
323        $cell->addText('Notification autorité de contrôle', $this->textHeadStyle);
324        $cell = $tableViolationAnnexeApplied->addCell(2000, $this->cellHeadStyle);
325        $cell->addText('Sous-traitants', $this->textHeadStyle);
326        $cell = $tableViolationAnnexeApplied->addCell(2000, $this->cellHeadStyle);
327        $cell->addText('Traitements associés', $this->textHeadStyle);
328
329        /** @var Violation $line */
330        foreach ($violations as $line) {
331            $tableViolationAnnexeApplied->addRow(null, ['cantSplit' => true]);
332            $cell = $tableViolationAnnexeApplied->addCell(1000);
333            $date = !$line->isInProgress() ? $line->getDate()->format('d/m/Y') : $line->getDate()->format('d/m/Y') . ' (En cours)';
334            $cell->addText($date);
335            $cell = $tableViolationAnnexeApplied->addCell(1000);
336            $cell->addText($line->getCommunication() ? ViolationCommunicationDictionary::getCommunications()[$line->getCommunication()] : '');
337            $cell = $tableViolationAnnexeApplied->addCell(2000);
338            $cell->addText($line->getNotification() ? ViolationNotificationDictionary::getNotifications()[$line->getNotification()] : '');
339            $cell = $tableViolationAnnexeApplied->addCell(2000);
340            foreach ($line->getContractors() as $item) {
341                $cell->addListItem($item->getName(), null, [], [], ['spaceAfter' => 0]);
342            }
343            $cell = $tableViolationAnnexeApplied->addCell(2000);
344            foreach ($line->getTreatments() as $item) {
345                $cell->addListItem($item->getName(), null, [], [], ['spaceAfter' => 0]);
346            }
347        }
348        $section->addPageBreak();
349    }
350}