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; |
||||||
24 | |||||||
25 | use App\DataTables\Filters\LogFilter; |
||||||
26 | use App\DataTables\LogDataTable; |
||||||
27 | use App\Entity\Base\AbstractDBElement; |
||||||
28 | use App\Entity\LogSystem\AbstractLogEntry; |
||||||
29 | use App\Entity\LogSystem\CollectionElementDeleted; |
||||||
30 | use App\Entity\LogSystem\ElementCreatedLogEntry; |
||||||
31 | use App\Entity\LogSystem\ElementDeletedLogEntry; |
||||||
32 | use App\Entity\LogSystem\ElementEditedLogEntry; |
||||||
33 | use App\Form\Filters\LogFilterType; |
||||||
34 | use App\Repository\DBElementRepository; |
||||||
35 | use App\Services\LogSystem\EventUndoHelper; |
||||||
36 | use App\Services\LogSystem\TimeTravel; |
||||||
37 | use Doctrine\ORM\EntityManagerInterface; |
||||||
38 | use Doctrine\ORM\EntityRepository; |
||||||
39 | use InvalidArgumentException; |
||||||
40 | use Omines\DataTablesBundle\DataTableFactory; |
||||||
41 | use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
||||||
42 | use Symfony\Component\HttpFoundation\JsonResponse; |
||||||
43 | use Symfony\Component\HttpFoundation\RedirectResponse; |
||||||
44 | use Symfony\Component\HttpFoundation\Request; |
||||||
45 | use Symfony\Component\HttpFoundation\Response; |
||||||
46 | use Symfony\Component\Routing\Annotation\Route; |
||||||
47 | |||||||
48 | /** |
||||||
49 | * @Route("/log") |
||||||
50 | */ |
||||||
51 | class LogController extends AbstractController |
||||||
52 | { |
||||||
53 | protected EntityManagerInterface $entityManager; |
||||||
54 | protected TimeTravel $timeTravel; |
||||||
55 | protected DBElementRepository $dbRepository; |
||||||
56 | |||||||
57 | public function __construct(EntityManagerInterface $entityManager, TimeTravel $timeTravel) |
||||||
58 | { |
||||||
59 | $this->entityManager = $entityManager; |
||||||
60 | $this->timeTravel = $timeTravel; |
||||||
61 | $this->dbRepository = $entityManager->getRepository(AbstractDBElement::class); |
||||||
62 | } |
||||||
63 | |||||||
64 | /** |
||||||
65 | * @Route("/", name="log_view") |
||||||
66 | * |
||||||
67 | * @return JsonResponse|Response |
||||||
68 | */ |
||||||
69 | public function showLogs(Request $request, DataTableFactory $dataTable) |
||||||
70 | { |
||||||
71 | $this->denyAccessUnlessGranted('@system.show_logs'); |
||||||
72 | |||||||
73 | $formRequest = clone $request; |
||||||
74 | $formRequest->setMethod('GET'); |
||||||
75 | $filter = new LogFilter(); |
||||||
76 | |||||||
77 | $filterForm = $this->createForm(LogFilterType::class, $filter, ['method' => 'GET']); |
||||||
78 | |||||||
79 | $filterForm->handleRequest($formRequest); |
||||||
80 | |||||||
81 | $table = $dataTable->createFromType(LogDataTable::class, [ |
||||||
82 | 'filter' => $filter, |
||||||
83 | ]) |
||||||
84 | ->handleRequest($request); |
||||||
85 | |||||||
86 | if ($table->isCallback()) { |
||||||
87 | return $table->getResponse(); |
||||||
88 | } |
||||||
89 | |||||||
90 | return $this->render('log_system/log_list.html.twig', [ |
||||||
91 | 'datatable' => $table, |
||||||
92 | 'filterForm' => $filterForm->createView(), |
||||||
93 | ]); |
||||||
94 | } |
||||||
95 | |||||||
96 | /** |
||||||
97 | * @Route("/undo", name="log_undo", methods={"POST"}) |
||||||
98 | */ |
||||||
99 | public function undoRevertLog(Request $request, EventUndoHelper $eventUndoHelper): RedirectResponse |
||||||
100 | { |
||||||
101 | $mode = EventUndoHelper::MODE_UNDO; |
||||||
102 | $id = $request->request->get('undo'); |
||||||
103 | |||||||
104 | //If no undo value was set check if a revert was set |
||||||
105 | if (null === $id) { |
||||||
106 | $id = $request->get('revert'); |
||||||
107 | $mode = EventUndoHelper::MODE_REVERT; |
||||||
108 | } |
||||||
109 | |||||||
110 | $log_element = $this->entityManager->find(AbstractLogEntry::class, $id); |
||||||
111 | if (null === $log_element) { |
||||||
112 | throw new InvalidArgumentException('No log entry with the given ID is existing!'); |
||||||
113 | } |
||||||
114 | |||||||
115 | $this->denyAccessUnlessGranted('revert_element', $log_element->getTargetClass()); |
||||||
116 | |||||||
117 | $eventUndoHelper->setMode($mode); |
||||||
118 | $eventUndoHelper->setUndoneEvent($log_element); |
||||||
119 | |||||||
120 | if (EventUndoHelper::MODE_UNDO === $mode) { |
||||||
121 | $this->undoLog($log_element); |
||||||
122 | } elseif (EventUndoHelper::MODE_REVERT === $mode) { |
||||||
123 | $this->revertLog($log_element); |
||||||
124 | } |
||||||
125 | |||||||
126 | $eventUndoHelper->clearUndoneEvent(); |
||||||
127 | |||||||
128 | $redirect = $request->request->get('redirect_back'); |
||||||
129 | |||||||
130 | return $this->redirect($redirect); |
||||||
131 | } |
||||||
132 | |||||||
133 | protected function revertLog(AbstractLogEntry $logEntry): void |
||||||
134 | { |
||||||
135 | $timestamp = $logEntry->getTimestamp(); |
||||||
136 | $element = $this->entityManager->find($logEntry->getTargetClass(), $logEntry->getTargetID()); |
||||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||
137 | //If the element is not available in DB try to undelete it |
||||||
138 | if (null === $element) { |
||||||
139 | $element = $this->timeTravel->undeleteEntity($logEntry->getTargetClass(), $logEntry->getTargetID()); |
||||||
0 ignored issues
–
show
It seems like
$logEntry->getTargetClass() can also be of type null ; however, parameter $class of App\Services\LogSystem\T...ravel::undeleteEntity() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() It seems like
$logEntry->getTargetID() can also be of type null ; however, parameter $id of App\Services\LogSystem\T...ravel::undeleteEntity() does only seem to accept integer , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
140 | $this->entityManager->persist($element); |
||||||
141 | $this->entityManager->flush(); |
||||||
142 | $this->dbRepository->changeID($element, $logEntry->getTargetID()); |
||||||
0 ignored issues
–
show
It seems like
$logEntry->getTargetID() can also be of type null ; however, parameter $new_id of App\Repository\DBElementRepository::changeID() does only seem to accept integer , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
143 | } |
||||||
144 | |||||||
145 | if (!$element instanceof AbstractDBElement) { |
||||||
146 | $this->addFlash('error', 'log.undo.target_not_found'); |
||||||
147 | |||||||
148 | return; |
||||||
149 | } |
||||||
150 | |||||||
151 | $this->timeTravel->revertEntityToTimestamp($element, $timestamp); |
||||||
152 | $this->entityManager->flush(); |
||||||
153 | $this->addFlash('success', 'log.undo.revert_success'); |
||||||
154 | } |
||||||
155 | |||||||
156 | protected function undoLog(AbstractLogEntry $log_element): void |
||||||
157 | { |
||||||
158 | if ($log_element instanceof ElementDeletedLogEntry || $log_element instanceof CollectionElementDeleted) { |
||||||
159 | if ($log_element instanceof ElementDeletedLogEntry) { |
||||||
160 | $element_class = $log_element->getTargetClass(); |
||||||
161 | $element_id = $log_element->getTargetID(); |
||||||
162 | } else { |
||||||
163 | $element_class = $log_element->getDeletedElementClass(); |
||||||
164 | $element_id = $log_element->getDeletedElementID(); |
||||||
165 | } |
||||||
166 | |||||||
167 | //Check if the element we want to undelete already exits |
||||||
168 | if (null === $this->entityManager->find($element_class, $element_id)) { |
||||||
0 ignored issues
–
show
It seems like
$element_class can also be of type null ; however, parameter $className of Doctrine\Persistence\ObjectManager::find() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
169 | $undeleted_element = $this->timeTravel->undeleteEntity($element_class, $element_id); |
||||||
0 ignored issues
–
show
It seems like
$element_class can also be of type null ; however, parameter $class of App\Services\LogSystem\T...ravel::undeleteEntity() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() It seems like
$element_id can also be of type null ; however, parameter $id of App\Services\LogSystem\T...ravel::undeleteEntity() does only seem to accept integer , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
170 | $this->entityManager->persist($undeleted_element); |
||||||
171 | $this->entityManager->flush(); |
||||||
172 | $this->dbRepository->changeID($undeleted_element, $element_id); |
||||||
0 ignored issues
–
show
It seems like
$element_id can also be of type null ; however, parameter $new_id of App\Repository\DBElementRepository::changeID() does only seem to accept integer , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
173 | $this->addFlash('success', 'log.undo.element_undelete_success'); |
||||||
174 | } else { |
||||||
175 | $this->addFlash('warning', 'log.undo.element_element_already_undeleted'); |
||||||
176 | } |
||||||
177 | } elseif ($log_element instanceof ElementCreatedLogEntry) { |
||||||
178 | $element = $this->entityManager->find($log_element->getTargetClass(), $log_element->getTargetID()); |
||||||
179 | if (null !== $element) { |
||||||
180 | $this->entityManager->remove($element); |
||||||
181 | $this->entityManager->flush(); |
||||||
182 | $this->addFlash('success', 'log.undo.element_delete_success'); |
||||||
183 | } else { |
||||||
184 | $this->addFlash('warning', 'log.undo.element.element_already_delted'); |
||||||
185 | } |
||||||
186 | } elseif ($log_element instanceof ElementEditedLogEntry) { |
||||||
187 | $element = $this->entityManager->find($log_element->getTargetClass(), $log_element->getTargetID()); |
||||||
188 | if ($element instanceof AbstractDBElement) { |
||||||
189 | $this->timeTravel->applyEntry($element, $log_element); |
||||||
190 | $this->entityManager->flush(); |
||||||
191 | $this->addFlash('success', 'log.undo.element_change_undone'); |
||||||
192 | } else { |
||||||
193 | $this->addFlash('error', 'log.undo.do_undelete_before'); |
||||||
194 | } |
||||||
195 | } else { |
||||||
196 | $this->addFlash('error', 'log.undo.log_type_invalid'); |
||||||
197 | } |
||||||
198 | } |
||||||
199 | } |
||||||
200 |