Issues (257)

src/Controller/LogController.php (8 issues)

Labels
Severity
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
It seems like $logEntry->getTargetClass() 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 ignore-type  annotation

136
        $element = $this->entityManager->find(/** @scrutinizer ignore-type */ $logEntry->getTargetClass(), $logEntry->getTargetID());
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 ignore-type  annotation

139
            $element = $this->timeTravel->undeleteEntity(/** @scrutinizer ignore-type */ $logEntry->getTargetClass(), $logEntry->getTargetID());
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 ignore-type  annotation

139
            $element = $this->timeTravel->undeleteEntity($logEntry->getTargetClass(), /** @scrutinizer ignore-type */ $logEntry->getTargetID());
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 ignore-type  annotation

142
            $this->dbRepository->changeID($element, /** @scrutinizer ignore-type */ $logEntry->getTargetID());
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 ignore-type  annotation

168
            if (null === $this->entityManager->find(/** @scrutinizer ignore-type */ $element_class, $element_id)) {
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 ignore-type  annotation

169
                $undeleted_element = $this->timeTravel->undeleteEntity(/** @scrutinizer ignore-type */ $element_class, $element_id);
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 ignore-type  annotation

169
                $undeleted_element = $this->timeTravel->undeleteEntity($element_class, /** @scrutinizer ignore-type */ $element_id);
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 ignore-type  annotation

172
                $this->dbRepository->changeID($undeleted_element, /** @scrutinizer ignore-type */ $element_id);
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