1 | <?php |
||||
2 | /** |
||||
3 | * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). |
||||
4 | * |
||||
5 | * Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics) |
||||
6 | * |
||||
7 | * This program is free software: you can redistribute it and/or modify |
||||
8 | * it under the terms of the GNU Affero General Public License as published |
||||
9 | * by the Free Software Foundation, either version 3 of the License, or |
||||
10 | * (at your option) any later version. |
||||
11 | * |
||||
12 | * This program is distributed in the hope that it will be useful, |
||||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
15 | * GNU Affero General Public License for more details. |
||||
16 | * |
||||
17 | * You should have received a copy of the GNU Affero General Public License |
||||
18 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
19 | */ |
||||
20 | |||||
21 | declare(strict_types=1); |
||||
22 | |||||
23 | namespace App\Controller\AdminPages; |
||||
24 | |||||
25 | use App\DataTables\LogDataTable; |
||||
26 | use App\Entity\Attachments\Attachment; |
||||
27 | use App\Entity\Base\AbstractDBElement; |
||||
28 | use App\Entity\Base\AbstractNamedDBElement; |
||||
29 | use App\Entity\Base\AbstractPartsContainingDBElement; |
||||
30 | use App\Entity\Base\AbstractStructuralDBElement; |
||||
31 | use App\Entity\Base\PartsContainingRepositoryInterface; |
||||
32 | use App\Entity\LabelSystem\LabelProfile; |
||||
33 | use App\Entity\Parameters\AbstractParameter; |
||||
34 | use App\Entity\UserSystem\User; |
||||
35 | use App\Exceptions\AttachmentDownloadException; |
||||
36 | use App\Form\AdminPages\ImportType; |
||||
37 | use App\Form\AdminPages\MassCreationForm; |
||||
38 | use App\Repository\AbstractPartsContainingRepository; |
||||
39 | use App\Services\Attachments\AttachmentSubmitHandler; |
||||
40 | use App\Services\ImportExportSystem\EntityExporter; |
||||
41 | use App\Services\ImportExportSystem\EntityImporter; |
||||
42 | use App\Services\LabelSystem\Barcodes\BarcodeExampleElementsGenerator; |
||||
43 | use App\Services\LabelSystem\LabelGenerator; |
||||
44 | use App\Services\LogSystem\EventCommentHelper; |
||||
45 | use App\Services\LogSystem\HistoryHelper; |
||||
46 | use App\Services\LogSystem\TimeTravel; |
||||
47 | use App\Services\Trees\StructuralElementRecursionHelper; |
||||
48 | use DateTime; |
||||
49 | use Doctrine\ORM\EntityManagerInterface; |
||||
50 | use InvalidArgumentException; |
||||
51 | use Omines\DataTablesBundle\DataTableFactory; |
||||
52 | use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
||||
53 | use Symfony\Component\EventDispatcher\EventDispatcher; |
||||
54 | use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
||||
55 | use Symfony\Component\Form\FormInterface; |
||||
56 | use Symfony\Component\HttpFoundation\File\UploadedFile; |
||||
57 | use Symfony\Component\HttpFoundation\RedirectResponse; |
||||
58 | use Symfony\Component\HttpFoundation\Request; |
||||
59 | use Symfony\Component\HttpFoundation\Response; |
||||
60 | use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; |
||||
61 | use Symfony\Component\Validator\ConstraintViolationList; |
||||
62 | use Symfony\Contracts\Translation\TranslatorInterface; |
||||
63 | |||||
64 | use function Symfony\Component\Translation\t; |
||||
65 | |||||
66 | abstract class BaseAdminController extends AbstractController |
||||
67 | { |
||||
68 | protected string $entity_class = ''; |
||||
69 | protected string $form_class = ''; |
||||
70 | protected string $twig_template = ''; |
||||
71 | protected string $route_base = ''; |
||||
72 | protected string $attachment_class = ''; |
||||
73 | protected ?string $parameter_class = ''; |
||||
74 | |||||
75 | protected UserPasswordHasherInterface $passwordEncoder; |
||||
76 | protected TranslatorInterface $translator; |
||||
77 | protected AttachmentSubmitHandler $attachmentSubmitHandler; |
||||
78 | protected EventCommentHelper $commentHelper; |
||||
79 | |||||
80 | protected HistoryHelper $historyHelper; |
||||
81 | protected TimeTravel $timeTravel; |
||||
82 | protected DataTableFactory $dataTableFactory; |
||||
83 | /** |
||||
84 | * @var EventDispatcher|EventDispatcherInterface |
||||
85 | */ |
||||
86 | protected $eventDispatcher; |
||||
87 | protected LabelGenerator $labelGenerator; |
||||
88 | protected BarcodeExampleElementsGenerator $barcodeExampleGenerator; |
||||
89 | |||||
90 | protected EntityManagerInterface $entityManager; |
||||
91 | |||||
92 | public function __construct(TranslatorInterface $translator, UserPasswordHasherInterface $passwordEncoder, |
||||
93 | AttachmentSubmitHandler $attachmentSubmitHandler, |
||||
94 | EventCommentHelper $commentHelper, HistoryHelper $historyHelper, TimeTravel $timeTravel, |
||||
95 | DataTableFactory $dataTableFactory, EventDispatcherInterface $eventDispatcher, BarcodeExampleElementsGenerator $barcodeExampleGenerator, |
||||
96 | LabelGenerator $labelGenerator, EntityManagerInterface $entityManager) |
||||
97 | { |
||||
98 | if ('' === $this->entity_class || '' === $this->form_class || '' === $this->twig_template || '' === $this->route_base) { |
||||
99 | throw new InvalidArgumentException('You have to override the $entity_class, $form_class, $route_base and $twig_template value in your subclasss!'); |
||||
100 | } |
||||
101 | |||||
102 | if ('' === $this->attachment_class || !is_a($this->attachment_class, Attachment::class, true)) { |
||||
103 | throw new InvalidArgumentException('You have to override the $attachment_class value with a valid Attachment class in your subclass!'); |
||||
104 | } |
||||
105 | |||||
106 | if ('' === $this->parameter_class || ($this->parameter_class && !is_a($this->parameter_class, AbstractParameter::class, true))) { |
||||
107 | throw new InvalidArgumentException('You have to override the $parameter_class value with a valid Parameter class in your subclass!'); |
||||
108 | } |
||||
109 | |||||
110 | $this->translator = $translator; |
||||
111 | $this->passwordEncoder = $passwordEncoder; |
||||
112 | $this->attachmentSubmitHandler = $attachmentSubmitHandler; |
||||
113 | $this->commentHelper = $commentHelper; |
||||
114 | $this->historyHelper = $historyHelper; |
||||
115 | $this->timeTravel = $timeTravel; |
||||
116 | $this->dataTableFactory = $dataTableFactory; |
||||
117 | $this->eventDispatcher = $eventDispatcher; |
||||
118 | $this->barcodeExampleGenerator = $barcodeExampleGenerator; |
||||
119 | $this->labelGenerator = $labelGenerator; |
||||
120 | $this->entityManager = $entityManager; |
||||
121 | } |
||||
122 | |||||
123 | protected function revertElementIfNeeded(AbstractDBElement $entity, ?string $timestamp): ?DateTime |
||||
124 | { |
||||
125 | if (null !== $timestamp) { |
||||
126 | $this->denyAccessUnlessGranted('show_history', $entity); |
||||
127 | //If the timestamp only contains numbers interpret it as unix timestamp |
||||
128 | if (ctype_digit($timestamp)) { |
||||
129 | $timeTravel_timestamp = new DateTime(); |
||||
130 | $timeTravel_timestamp->setTimestamp((int) $timestamp); |
||||
131 | } else { //Try to parse it via DateTime |
||||
132 | $timeTravel_timestamp = new DateTime($timestamp); |
||||
133 | } |
||||
134 | $this->timeTravel->revertEntityToTimestamp($entity, $timeTravel_timestamp); |
||||
135 | |||||
136 | return $timeTravel_timestamp; |
||||
137 | } |
||||
138 | |||||
139 | return null; |
||||
140 | } |
||||
141 | |||||
142 | /** |
||||
143 | * Perform some additional actions, when the form was valid, but before the entity is saved. |
||||
144 | * |
||||
145 | * @return bool return true, to save entity normally, return false, to abort saving |
||||
146 | */ |
||||
147 | protected function additionalActionEdit(FormInterface $form, AbstractNamedDBElement $entity): bool |
||||
0 ignored issues
–
show
|
|||||
148 | { |
||||
149 | return true; |
||||
150 | } |
||||
151 | |||||
152 | protected function _edit(AbstractNamedDBElement $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response |
||||
153 | { |
||||
154 | $this->denyAccessUnlessGranted('read', $entity); |
||||
155 | |||||
156 | $timeTravel_timestamp = $this->revertElementIfNeeded($entity, $timestamp); |
||||
157 | |||||
158 | if ($this->isGranted('show_history', $entity)) { |
||||
159 | $table = $this->dataTableFactory->createFromType( |
||||
160 | LogDataTable::class, |
||||
161 | [ |
||||
162 | 'filter_elements' => $this->historyHelper->getAssociatedElements($entity), |
||||
163 | 'mode' => 'element_history', |
||||
164 | ], |
||||
165 | ['pageLength' => 10] |
||||
166 | ) |
||||
167 | ->handleRequest($request); |
||||
168 | |||||
169 | if ($table->isCallback()) { |
||||
170 | return $table->getResponse(); |
||||
171 | } |
||||
172 | } else { |
||||
173 | $table = null; |
||||
174 | } |
||||
175 | |||||
176 | $form_options = [ |
||||
177 | 'attachment_class' => $this->attachment_class, |
||||
178 | 'parameter_class' => $this->parameter_class, |
||||
179 | 'disabled' => null !== $timeTravel_timestamp, |
||||
180 | ]; |
||||
181 | |||||
182 | //Disable editing of options, if user is not allowed to use twig... |
||||
183 | if ( |
||||
184 | $entity instanceof LabelProfile |
||||
185 | && 'twig' === $entity->getOptions()->getLinesMode() |
||||
186 | && !$this->isGranted('@labels.use_twig') |
||||
187 | ) { |
||||
188 | $form_options['disable_options'] = true; |
||||
189 | } |
||||
190 | |||||
191 | $form = $this->createForm($this->form_class, $entity, $form_options); |
||||
192 | |||||
193 | $form->handleRequest($request); |
||||
194 | if ($form->isSubmitted() && $form->isValid()) { |
||||
195 | if ($this->additionalActionEdit($form, $entity)) { |
||||
196 | //Upload passed files |
||||
197 | $attachments = $form['attachments']; |
||||
198 | foreach ($attachments as $attachment) { |
||||
199 | /** @var FormInterface $attachment */ |
||||
200 | $options = [ |
||||
201 | 'secure_attachment' => $attachment['secureFile']->getData(), |
||||
202 | 'download_url' => $attachment['downloadURL']->getData(), |
||||
203 | ]; |
||||
204 | |||||
205 | try { |
||||
206 | $this->attachmentSubmitHandler->handleFormSubmit( |
||||
207 | $attachment->getData(), |
||||
208 | $attachment['file']->getData(), |
||||
209 | $options |
||||
210 | ); |
||||
211 | } catch (AttachmentDownloadException $attachmentDownloadException) { |
||||
212 | $this->addFlash( |
||||
213 | 'error', |
||||
214 | $this->translator->trans( |
||||
215 | 'attachment.download_failed' |
||||
216 | ).' '.$attachmentDownloadException->getMessage() |
||||
217 | ); |
||||
218 | } |
||||
219 | } |
||||
220 | |||||
221 | $this->commentHelper->setMessage($form['log_comment']->getData()); |
||||
222 | |||||
223 | $em->persist($entity); |
||||
224 | $em->flush(); |
||||
225 | $this->addFlash('success', 'entity.edit_flash'); |
||||
226 | } |
||||
227 | |||||
228 | //Rebuild form, so it is based on the updated data. Important for the parent field! |
||||
229 | //We can not use dynamic form events here, because the parent entity list is build from database! |
||||
230 | $form = $this->createForm($this->form_class, $entity, [ |
||||
231 | 'attachment_class' => $this->attachment_class, |
||||
232 | 'parameter_class' => $this->parameter_class, |
||||
233 | ]); |
||||
234 | } elseif ($form->isSubmitted() && !$form->isValid()) { |
||||
235 | $this->addFlash('error', 'entity.edit_flash.invalid'); |
||||
236 | } |
||||
237 | |||||
238 | //Show preview for LabelProfile if needed. |
||||
239 | if ($entity instanceof LabelProfile) { |
||||
240 | $example = $this->barcodeExampleGenerator->getElement($entity->getOptions()->getSupportedElement()); |
||||
241 | $pdf_data = $this->labelGenerator->generateLabel($entity->getOptions(), $example); |
||||
242 | } |
||||
243 | |||||
244 | /** @var AbstractPartsContainingRepository $repo */ |
||||
245 | $repo = $this->entityManager->getRepository($this->entity_class); |
||||
246 | |||||
247 | return $this->renderForm($this->twig_template, [ |
||||
248 | 'entity' => $entity, |
||||
249 | 'form' => $form, |
||||
250 | 'route_base' => $this->route_base, |
||||
251 | 'datatable' => $table, |
||||
252 | 'pdf_data' => $pdf_data ?? null, |
||||
253 | 'timeTravel' => $timeTravel_timestamp, |
||||
254 | 'repo' => $repo, |
||||
255 | 'partsContainingElement' => $repo instanceof PartsContainingRepositoryInterface, |
||||
256 | ]); |
||||
257 | } |
||||
258 | |||||
259 | /** |
||||
260 | * Perform some additional actions, when the form was valid, but before the entity is saved. |
||||
261 | * |
||||
262 | * @return bool return true, to save entity normally, return false, to abort saving |
||||
263 | */ |
||||
264 | protected function additionalActionNew(FormInterface $form, AbstractNamedDBElement $entity): bool |
||||
0 ignored issues
–
show
The parameter
$form is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||
265 | { |
||||
266 | return true; |
||||
267 | } |
||||
268 | |||||
269 | protected function _new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?AbstractNamedDBElement $entity = null) |
||||
270 | { |
||||
271 | $master_picture_backup = null; |
||||
0 ignored issues
–
show
|
|||||
272 | if (null === $entity) { |
||||
273 | /** @var AbstractStructuralDBElement|User $new_entity */ |
||||
274 | $new_entity = new $this->entity_class(); |
||||
275 | } else { |
||||
276 | /** @var AbstractStructuralDBElement|User $new_entity */ |
||||
277 | $new_entity = clone $entity; |
||||
278 | } |
||||
279 | |||||
280 | $this->denyAccessUnlessGranted('read', $new_entity); |
||||
281 | |||||
282 | //Basic edit form |
||||
283 | $form = $this->createForm($this->form_class, $new_entity, [ |
||||
284 | 'attachment_class' => $this->attachment_class, |
||||
285 | 'parameter_class' => $this->parameter_class, |
||||
286 | ]); |
||||
287 | |||||
288 | $form->handleRequest($request); |
||||
289 | |||||
290 | if ($form->isSubmitted() && $form->isValid()) { |
||||
291 | //Perform additional actions |
||||
292 | if ($this->additionalActionNew($form, $new_entity)) { |
||||
293 | //Upload passed files |
||||
294 | $attachments = $form['attachments']; |
||||
295 | foreach ($attachments as $attachment) { |
||||
296 | /** @var FormInterface $attachment */ |
||||
297 | $options = [ |
||||
298 | 'secure_attachment' => $attachment['secureFile']->getData(), |
||||
299 | 'download_url' => $attachment['downloadURL']->getData(), |
||||
300 | ]; |
||||
301 | |||||
302 | try { |
||||
303 | $this->attachmentSubmitHandler->handleFormSubmit( |
||||
304 | $attachment->getData(), |
||||
305 | $attachment['file']->getData(), |
||||
306 | $options |
||||
307 | ); |
||||
308 | } catch (AttachmentDownloadException $attachmentDownloadException) { |
||||
309 | $this->addFlash( |
||||
310 | 'error', |
||||
311 | $this->translator->trans( |
||||
312 | 'attachment.download_failed' |
||||
313 | ).' '.$attachmentDownloadException->getMessage() |
||||
314 | ); |
||||
315 | } |
||||
316 | } |
||||
317 | |||||
318 | $this->commentHelper->setMessage($form['log_comment']->getData()); |
||||
319 | |||||
320 | $em->persist($new_entity); |
||||
321 | $em->flush(); |
||||
322 | $this->addFlash('success', 'entity.created_flash'); |
||||
323 | |||||
324 | return $this->redirectToRoute($this->route_base.'_edit', ['id' => $new_entity->getID()]); |
||||
325 | } |
||||
326 | } |
||||
327 | |||||
328 | if ($form->isSubmitted() && !$form->isValid()) { |
||||
329 | $this->addFlash('error', 'entity.created_flash.invalid'); |
||||
330 | } |
||||
331 | |||||
332 | //Import form |
||||
333 | $import_form = $this->createForm(ImportType::class, ['entity_class' => $this->entity_class]); |
||||
334 | $import_form->handleRequest($request); |
||||
335 | |||||
336 | if ($import_form->isSubmitted() && $import_form->isValid()) { |
||||
337 | /** @var UploadedFile $file */ |
||||
338 | $file = $import_form['file']->getData(); |
||||
339 | $data = $import_form->getData(); |
||||
340 | |||||
341 | $options = [ |
||||
342 | 'parent' => $data['parent'], |
||||
343 | 'preserve_children' => $data['preserve_children'], |
||||
344 | 'format' => $data['format'], |
||||
345 | 'csv_separator' => $data['csv_separator'], |
||||
346 | ]; |
||||
347 | |||||
348 | $this->commentHelper->setMessage('Import '.$file->getClientOriginalName()); |
||||
349 | |||||
350 | $errors = $importer->fileToDBEntities($file, $this->entity_class, $options); |
||||
351 | |||||
352 | foreach ($errors as $name => $error) { |
||||
353 | /** @var ConstraintViolationList $error */ |
||||
354 | $this->addFlash('error', $name.':'.$error); |
||||
355 | } |
||||
356 | } |
||||
357 | |||||
358 | //Mass creation form |
||||
359 | $mass_creation_form = $this->createForm(MassCreationForm::class, ['entity_class' => $this->entity_class]); |
||||
360 | $mass_creation_form->handleRequest($request); |
||||
361 | |||||
362 | if ($mass_creation_form->isSubmitted() && $mass_creation_form->isValid()) { |
||||
363 | $data = $mass_creation_form->getData(); |
||||
364 | |||||
365 | //Create entries based on input |
||||
366 | $errors = []; |
||||
367 | $results = $importer->massCreation($data['lines'], $this->entity_class, $data['parent'] ?? null, $errors); |
||||
368 | |||||
369 | //Show errors to user: |
||||
370 | foreach ($errors as $error) { |
||||
371 | if ($error['entity'] instanceof AbstractStructuralDBElement) { |
||||
372 | $this->addFlash('error', $error['entity']->getFullPath().':'.$error['violations']); |
||||
373 | } else { //When we dont have a structural element, we can only show the name |
||||
374 | $this->addFlash('error', $error['entity']->getName().':'.$error['violations']); |
||||
375 | } |
||||
376 | } |
||||
377 | |||||
378 | //Persist valid entities to DB |
||||
379 | foreach ($results as $result) { |
||||
380 | $em->persist($result); |
||||
381 | } |
||||
382 | $em->flush(); |
||||
383 | } |
||||
384 | |||||
385 | return $this->renderForm($this->twig_template, [ |
||||
386 | 'entity' => $new_entity, |
||||
387 | 'form' => $form, |
||||
388 | 'import_form' => $import_form, |
||||
389 | 'mass_creation_form' => $mass_creation_form, |
||||
390 | 'route_base' => $this->route_base, |
||||
391 | ]); |
||||
392 | } |
||||
393 | |||||
394 | /** |
||||
395 | * Performs checks if the element can be deleted safely. Otherwise an flash message is added. |
||||
396 | * |
||||
397 | * @param AbstractNamedDBElement $entity the element that should be checked |
||||
398 | * |
||||
399 | * @return bool True if the the element can be deleted, false if not |
||||
400 | */ |
||||
401 | protected function deleteCheck(AbstractNamedDBElement $entity): bool |
||||
402 | { |
||||
403 | if ($entity instanceof AbstractPartsContainingDBElement) { |
||||
404 | /** @var AbstractPartsContainingRepository $repo */ |
||||
405 | $repo = $this->entityManager->getRepository($this->entity_class); |
||||
406 | if ($repo->getPartsCount($entity) > 0) { |
||||
407 | $this->addFlash('error', t('entity.delete.must_not_contain_parts', ['%PATH%' => $entity->getFullPath()])); |
||||
408 | |||||
409 | return false; |
||||
410 | } |
||||
411 | } |
||||
412 | |||||
413 | return true; |
||||
414 | } |
||||
415 | |||||
416 | protected function _delete(Request $request, AbstractNamedDBElement $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse |
||||
417 | { |
||||
418 | $this->denyAccessUnlessGranted('delete', $entity); |
||||
419 | |||||
420 | if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) { |
||||
421 | |||||
422 | $entityManager = $this->entityManager; |
||||
423 | |||||
424 | if (!$this->deleteCheck($entity)) { |
||||
425 | return $this->redirectToRoute($this->route_base.'_edit', ['id' => $entity->getID()]); |
||||
426 | } |
||||
427 | |||||
428 | //Check if we need to remove recursively |
||||
429 | if ($entity instanceof AbstractStructuralDBElement && $request->get('delete_recursive', false)) { |
||||
430 | $can_delete = true; |
||||
431 | //Check if any of the children can not be deleted, cause it contains parts |
||||
432 | $recursionHelper->execute($entity, function (AbstractStructuralDBElement $element) use (&$can_delete) { |
||||
433 | if(!$this->deleteCheck($element)) { |
||||
434 | $can_delete = false; |
||||
435 | } |
||||
436 | }); |
||||
437 | if($can_delete) { |
||||
438 | $recursionHelper->delete($entity, false); |
||||
439 | } else { |
||||
440 | return $this->redirectToRoute($this->route_base.'_edit', ['id' => $entity->getID()]); |
||||
441 | } |
||||
442 | } else { |
||||
443 | if ($entity instanceof AbstractStructuralDBElement) { |
||||
444 | $parent = $entity->getParent(); |
||||
445 | |||||
446 | //Move all sub entities to the current parent |
||||
447 | foreach ($entity->getSubelements() as $subelement) { |
||||
448 | $subelement->setParent($parent); |
||||
449 | $entityManager->persist($subelement); |
||||
450 | } |
||||
451 | } |
||||
452 | |||||
453 | //Remove current element |
||||
454 | $entityManager->remove($entity); |
||||
455 | } |
||||
456 | |||||
457 | $this->commentHelper->setMessage($request->request->get('log_comment', null)); |
||||
458 | |||||
459 | //Flush changes |
||||
460 | $entityManager->flush(); |
||||
461 | |||||
462 | $this->addFlash('success', 'attachment_type.deleted'); |
||||
463 | } else { |
||||
464 | $this->addFlash('error', 'csfr_invalid'); |
||||
465 | } |
||||
466 | |||||
467 | return $this->redirectToRoute($this->route_base.'_new'); |
||||
468 | } |
||||
469 | |||||
470 | protected function _exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response |
||||
471 | { |
||||
472 | $entity = new $this->entity_class(); |
||||
473 | $this->denyAccessUnlessGranted('read', $entity); |
||||
474 | $entities = $em->getRepository($this->entity_class)->findAll(); |
||||
475 | |||||
476 | return $exporter->exportEntityFromRequest($entities, $request); |
||||
477 | } |
||||
478 | |||||
479 | protected function _exportEntity(AbstractNamedDBElement $entity, EntityExporter $exporter, Request $request): Response |
||||
480 | { |
||||
481 | $this->denyAccessUnlessGranted('read', $entity); |
||||
482 | |||||
483 | return $exporter->exportEntityFromRequest($entity, $request); |
||||
484 | } |
||||
485 | } |
||||
486 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.