Passed
Push — master ( 2d425f...eb03d1 )
by Jan
04:57 queued 10s
created

LogController::undoLog()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 41
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 33
c 0
b 0
f 0
nc 9
nop 1
dl 0
loc 41
rs 8.0555
1
<?php
2
/**
3
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
4
 *
5
 * Copyright (C) 2019 - 2020 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
/**
24
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
25
 *
26
 * Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
27
 *
28
 * This program is free software; you can redistribute it and/or
29
 * modify it under the terms of the GNU General Public License
30
 * as published by the Free Software Foundation; either version 2
31
 * of the License, or (at your option) any later version.
32
 *
33
 * This program is distributed in the hope that it will be useful,
34
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36
 * GNU General Public License for more details.
37
 *
38
 * You should have received a copy of the GNU General Public License
39
 * along with this program; if not, write to the Free Software
40
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
41
 */
42
43
namespace App\Controller;
44
45
use App\DataTables\LogDataTable;
46
use App\Entity\Base\AbstractDBElement;
47
use App\Entity\LogSystem\AbstractLogEntry;
48
use App\Entity\LogSystem\CollectionElementDeleted;
49
use App\Entity\LogSystem\ElementCreatedLogEntry;
50
use App\Entity\LogSystem\ElementDeletedLogEntry;
51
use App\Entity\LogSystem\ElementEditedLogEntry;
52
use App\Services\LogSystem\EventUndoHelper;
53
use App\Services\LogSystem\TimeTravel;
54
use Doctrine\ORM\EntityManagerInterface;
55
use Omines\DataTablesBundle\DataTableFactory;
56
use phpDocumentor\Reflection\Element;
57
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
58
use Symfony\Component\HttpFoundation\JsonResponse;
59
use Symfony\Component\HttpFoundation\RedirectResponse;
60
use Symfony\Component\HttpFoundation\Request;
61
use Symfony\Component\HttpFoundation\Response;
62
use Symfony\Component\Routing\Annotation\Route;
63
64
/**
65
 * @Route("/log")
66
 */
67
class LogController extends AbstractController
68
{
69
    protected $entityManager;
70
    protected $timeTravel;
71
    protected $dbRepository;
72
73
74
    public function __construct(EntityManagerInterface $entityManager, TimeTravel $timeTravel)
75
    {
76
        $this->entityManager = $entityManager;
77
        $this->timeTravel = $timeTravel;
78
        $this->dbRepository = $entityManager->getRepository(AbstractDBElement::class);
79
    }
80
81
    /**
82
     * @Route("/", name="log_view")
83
     *
84
     * @param  Request  $request
85
     * @param  DataTableFactory  $dataTable
86
     * @return JsonResponse|Response
87
     */
88
    public function showLogs(Request $request, DataTableFactory $dataTable)
89
    {
90
        $this->denyAccessUnlessGranted('@system.show_logs');
91
92
        $table = $dataTable->createFromType(LogDataTable::class)
93
            ->handleRequest($request);
94
95
        if ($table->isCallback()) {
96
            return $table->getResponse();
97
        }
98
99
        return $this->render('LogSystem/log_list.html.twig', [
100
            'datatable' => $table,
101
        ]);
102
    }
103
104
    /**
105
     * @Route("/undo", name="log_undo", methods={"POST"})
106
     * @param  Request  $request
107
     */
108
    public function undoRevertLog(Request $request, EventUndoHelper $eventUndoHelper)
109
    {
110
        $mode = EventUndoHelper::MODE_UNDO;
111
        $id = $request->request->get('undo');
112
113
        //If no undo value was set check if a revert was set
114
        if ($id === null) {
115
            $id = $request->get('revert');
116
            $mode = EventUndoHelper::MODE_REVERT;
117
        }
118
119
        $log_element = $this->entityManager->find(AbstractLogEntry::class, $id);
120
        if ($log_element === null) {
121
            throw new \InvalidArgumentException('No log entry with the given ID is existing!');
122
        }
123
124
        $this->denyAccessUnlessGranted('revert_element', $log_element->getTargetClass());
125
126
        $eventUndoHelper->setMode($mode);
127
        $eventUndoHelper->setUndoneEvent($log_element);
128
129
        if ($mode === EventUndoHelper::MODE_UNDO) {
130
            $this->undoLog($log_element);
131
        } elseif ($mode === EventUndoHelper::MODE_REVERT) {
132
            $this->revertLog($log_element);
133
        }
134
135
        $eventUndoHelper->clearUndoneEvent();
136
137
        $redirect = $request->request->get('redirect_back');
138
        return $this->redirect($redirect);
139
    }
140
141
    protected function revertLog(AbstractLogEntry $logEntry): void
142
    {
143
        $timestamp = $logEntry->getTimestamp();
144
        $element = $this->entityManager->find($logEntry->getTargetClass(), $logEntry->getTargetID());
145
        //If the element is not available in DB try to undelete it
146
        if ($element === null) {
147
            $element = $this->timeTravel->undeleteEntity($logEntry->getTargetClass(), $logEntry->getTargetID());
0 ignored issues
show
Bug introduced by
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

147
            $element = $this->timeTravel->undeleteEntity(/** @scrutinizer ignore-type */ $logEntry->getTargetClass(), $logEntry->getTargetID());
Loading history...
Bug introduced by
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

147
            $element = $this->timeTravel->undeleteEntity($logEntry->getTargetClass(), /** @scrutinizer ignore-type */ $logEntry->getTargetID());
Loading history...
148
            $this->entityManager->persist($element);
149
            $this->entityManager->flush();
150
            $this->dbRepository->changeID($element, $logEntry->getTargetID());
0 ignored issues
show
Bug introduced by
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

150
            $this->dbRepository->changeID($element, /** @scrutinizer ignore-type */ $logEntry->getTargetID());
Loading history...
151
        }
152
153
        if (!$element instanceof AbstractDBElement) {
154
            $this->addFlash('error', 'log.undo.target_not_found');
155
            return;
156
        }
157
158
        $this->timeTravel->revertEntityToTimestamp($element, $timestamp);
159
        $this->entityManager->flush();
160
        $this->addFlash('success', 'log.undo.revert_success');
161
    }
162
163
    protected function undoLog(AbstractLogEntry $log_element): void
164
    {
165
        if ($log_element instanceof ElementDeletedLogEntry || $log_element instanceof CollectionElementDeleted) {
166
            if ($log_element instanceof ElementDeletedLogEntry) {
167
                $element_class = $log_element->getTargetClass();
168
                $element_id = $log_element->getTargetID();
169
            } else {
170
                $element_class = $log_element->getDeletedElementClass();
171
                $element_id = $log_element->getDeletedElementID();
172
            }
173
174
            //Check if the element we want to undelete already exits
175
            if ($this->entityManager->find($element_class, $element_id) == null) {
176
                $undeleted_element = $this->timeTravel->undeleteEntity($element_class, $element_id);
0 ignored issues
show
Bug introduced by
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

176
                $undeleted_element = $this->timeTravel->undeleteEntity(/** @scrutinizer ignore-type */ $element_class, $element_id);
Loading history...
Bug introduced by
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

176
                $undeleted_element = $this->timeTravel->undeleteEntity($element_class, /** @scrutinizer ignore-type */ $element_id);
Loading history...
177
                $this->entityManager->persist($undeleted_element);
178
                $this->entityManager->flush();
179
                $this->dbRepository->changeID($undeleted_element, $element_id);
0 ignored issues
show
Bug introduced by
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

179
                $this->dbRepository->changeID($undeleted_element, /** @scrutinizer ignore-type */ $element_id);
Loading history...
180
                $this->addFlash('success', 'log.undo.element_undelete_success');
181
            } else {
182
                $this->addFlash('warning', 'log.undo.element_element_already_undeleted');
183
            }
184
        } elseif ($log_element instanceof ElementCreatedLogEntry) {
185
            $element = $this->entityManager->find($log_element->getTargetClass(), $log_element->getTargetID());
186
            if ($element !== null) {
187
                $this->entityManager->remove($element);
188
                $this->entityManager->flush();
189
                $this->addFlash('success', 'log.undo.element_delete_success');
190
            } else {
191
                $this->addFlash('warning', 'log.undo.element.element_already_delted');
192
            }
193
        } elseif ($log_element instanceof ElementEditedLogEntry) {
194
            $element = $this->entityManager->find($log_element->getTargetClass(), $log_element->getTargetID());
195
            if ($element instanceof AbstractDBElement) {
196
                $this->timeTravel->applyEntry($element, $log_element);
197
                $this->entityManager->flush();
198
                $this->addFlash('success', 'log.undo.element_change_undone');
199
            } else {
200
                $this->addFlash('error', 'log.undo.do_undelete_before');
201
            }
202
        } else {
203
            $this->addFlash('error', 'log.undo.log_type_invalid');
204
        }
205
    }
206
}
207