Part-DB /
Part-DB-symfony
| 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
Loading history...
|
|||||||
| 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
Loading history...
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
Loading history...
|
|||||||
| 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
Loading history...
|
|||||||
| 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
Loading history...
|
|||||||
| 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
Loading history...
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
Loading history...
|
|||||||
| 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
Loading history...
|
|||||||
| 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 |