Passed
Push — master ( e1a3a6...f09ddb )
by Jan
04:37
created

PartController::clone()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 14
nc 2
nop 4
dl 0
loc 24
rs 9.7998
c 0
b 0
f 0
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 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\Parts\Category;
47
use App\Entity\Parts\Part;
48
use App\Exceptions\AttachmentDownloadException;
49
use App\Form\Part\PartBaseType;
50
use App\Services\Attachments\AttachmentManager;
51
use App\Services\Attachments\AttachmentSubmitHandler;
52
use App\Services\Attachments\PartPreviewGenerator;
53
use App\Services\LogSystem\EventCommentHelper;
54
use App\Services\LogSystem\HistoryHelper;
55
use App\Services\LogSystem\TimeTravel;
56
use App\Services\PricedetailHelper;
57
use Doctrine\ORM\EntityManagerInterface;
58
use Omines\DataTablesBundle\DataTableFactory;
59
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
60
use Symfony\Component\Form\FormInterface;
61
use Symfony\Component\HttpFoundation\RedirectResponse;
62
use Symfony\Component\HttpFoundation\Request;
63
use Symfony\Component\HttpFoundation\Response;
64
use Symfony\Component\Routing\Annotation\Route;
65
use Symfony\Contracts\Translation\TranslatorInterface;
66
67
/**
68
 * @Route("/part")
69
 */
70
class PartController extends AbstractController
71
{
72
    protected $attachmentManager;
73
    protected $pricedetailHelper;
74
    protected $partPreviewGenerator;
75
    protected $commentHelper;
76
77
    public function __construct(AttachmentManager $attachmentManager, PricedetailHelper $pricedetailHelper,
78
        PartPreviewGenerator $partPreviewGenerator, EventCommentHelper $commentHelper)
79
    {
80
        $this->attachmentManager = $attachmentManager;
81
        $this->pricedetailHelper = $pricedetailHelper;
82
        $this->partPreviewGenerator = $partPreviewGenerator;
83
        $this->commentHelper = $commentHelper;
84
    }
85
86
    /**
87
     * @Route("/{id}/info/{timestamp}", name="part_info")
88
     * @Route("/{id}", requirements={"id"="\d+"})
89
     *
90
     * @param  Part  $part
91
     * @return Response
92
     * @throws \Exception
93
     */
94
    public function show(Part $part, Request $request, TimeTravel $timeTravel, HistoryHelper $historyHelper,
95
        DataTableFactory $dataTable, ?string $timestamp = null): Response
96
    {
97
        $this->denyAccessUnlessGranted('read', $part);
98
99
        $timeTravel_timestamp = null;
100
        if ($timestamp !== null) {
101
            $this->denyAccessUnlessGranted('@tools.timetravel');
102
            $this->denyAccessUnlessGranted('show_history', $part);
103
            //If the timestamp only contains numbers interpret it as unix timestamp
104
            if (ctype_digit($timestamp)) {
105
                $timeTravel_timestamp = new \DateTime();
106
                $timeTravel_timestamp->setTimestamp((int) $timestamp);
107
            } else { //Try to parse it via DateTime
108
                $timeTravel_timestamp = new \DateTime($timestamp);
109
            }
110
            $timeTravel->revertEntityToTimestamp($part, $timeTravel_timestamp);
111
        }
112
113
        if ($this->isGranted('show_history', $part) ) {
114
            $table = $dataTable->createFromType(LogDataTable::class, [
115
                'filter_elements' => $historyHelper->getAssociatedElements($part),
116
                'mode' => 'element_history'
117
            ], ['pageLength' => 10])
118
                ->handleRequest($request);
119
120
            if ($table->isCallback()) {
121
                return $table->getResponse();
122
            }
123
        } else {
124
            $table = null;
125
        }
126
127
        return $this->render(
128
            'Parts/info/show_part_info.html.twig',
129
            [
130
                'part' => $part,
131
                'datatable' => $table,
132
                'attachment_helper' => $this->attachmentManager,
133
                'pricedetail_helper' => $this->pricedetailHelper,
134
                'pictures' => $this->partPreviewGenerator->getPreviewAttachments($part),
135
                'timeTravel' => $timeTravel_timestamp
136
            ]
137
        );
138
    }
139
140
    /**
141
     * @Route("/{id}/edit", name="part_edit")
142
     *
143
     * @param  Part  $part
144
     * @param  Request  $request
145
     * @param  EntityManagerInterface  $em
146
     * @param  TranslatorInterface  $translator
147
     * @param  AttachmentSubmitHandler  $attachmentSubmitHandler
148
     * @return Response
149
     */
150
    public function edit(Part $part, Request $request, EntityManagerInterface $em, TranslatorInterface $translator,
151
        AttachmentSubmitHandler $attachmentSubmitHandler): Response
152
    {
153
        $this->denyAccessUnlessGranted('edit', $part);
154
155
        $form = $this->createForm(PartBaseType::class, $part);
156
157
        $form->handleRequest($request);
158
        if ($form->isSubmitted() && $form->isValid()) {
159
            //Upload passed files
160
            $attachments = $form['attachments'];
161
            foreach ($attachments as $attachment) {
162
                /** @var FormInterface $attachment */
163
                $options = [
164
                    'secure_attachment' => $attachment['secureFile']->getData(),
165
                    'download_url' => $attachment['downloadURL']->getData(),
166
                ];
167
168
                try {
169
                    $attachmentSubmitHandler->handleFormSubmit($attachment->getData(), $attachment['file']->getData(), $options);
170
                } catch (AttachmentDownloadException $attachmentDownloadException) {
171
                    $this->addFlash(
172
                        'error',
173
                        $translator->trans('attachment.download_failed').' '.$attachmentDownloadException->getMessage()
174
                    );
175
                }
176
            }
177
178
            $this->commentHelper->setMessage($form['log_comment']->getData());
179
180
            $em->persist($part);
181
            $em->flush();
182
            $this->addFlash('info', 'part.edited_flash');
183
            //Reload form, so the SIUnitType entries use the new part unit
184
            $form = $this->createForm(PartBaseType::class, $part);
185
        } elseif ($form->isSubmitted() && ! $form->isValid()) {
186
            $this->addFlash('error', 'part.edited_flash.invalid');
187
        }
188
189
        return $this->render('Parts/edit/edit_part_info.html.twig',
190
                             [
191
                                 'part' => $part,
192
                                 'form' => $form->createView(),
193
                                 'attachment_helper' => $this->attachmentManager,
194
                             ]);
195
    }
196
197
    /**
198
     * @Route("/{id}/delete", name="part_delete", methods={"DELETE"})
199
     *
200
     * @param  Request  $request
201
     * @param  Part  $part
202
     * @return RedirectResponse
203
     */
204
    public function delete(Request $request, Part $part): RedirectResponse
205
    {
206
        $this->denyAccessUnlessGranted('delete', $part);
207
208
        if ($this->isCsrfTokenValid('delete'.$part->getId(), $request->request->get('_token'))) {
209
            $entityManager = $this->getDoctrine()->getManager();
210
211
            $this->commentHelper->setMessage($request->request->get('log_comment', null));
212
213
            //Remove part
214
            $entityManager->remove($part);
215
216
            //Flush changes
217
            $entityManager->flush();
218
219
            $this->addFlash('success', 'part.deleted');
220
        }
221
222
        return $this->redirectToRoute('homepage');
223
    }
224
225
    /**
226
     * @Route("/new", name="part_new")
227
     * @Route("/{id}/clone", name="part_clone")
228
     *
229
     * @param  Request  $request
230
     * @param  EntityManagerInterface  $em
231
     * @param  TranslatorInterface  $translator
232
     * @param  AttachmentManager  $attachmentHelper
233
     * @param  AttachmentSubmitHandler  $attachmentSubmitHandler
234
     * @return Response
235
     */
236
    public function new(Request $request, EntityManagerInterface $em, TranslatorInterface $translator,
237
        AttachmentManager $attachmentHelper, AttachmentSubmitHandler $attachmentSubmitHandler, ?Part $part = null): Response
238
    {
239
        if($part === null) {
240
            $new_part = new Part();
241
        } else {
242
            $new_part = clone $part;
243
        }
244
245
        $this->denyAccessUnlessGranted('create', $new_part);
246
247
        $cid = $request->get('cid', 1);
248
249
        $category = $em->find(Category::class, $cid);
250
        if (null !== $category && $new_part->getCategory() === null) {
251
            $new_part->setCategory($category);
252
        }
253
254
        $form = $this->createForm(PartBaseType::class, $new_part);
255
256
        $form->handleRequest($request);
257
258
        if ($form->isSubmitted() && $form->isValid()) {
259
            //Upload passed files
260
            $attachments = $form['attachments'];
261
            foreach ($attachments as $attachment) {
262
                /** @var FormInterface $attachment */
263
                $options = [
264
                    'secure_attachment' => $attachment['secureFile']->getData(),
265
                    'download_url' => $attachment['downloadURL']->getData(),
266
                ];
267
268
                try {
269
                    $attachmentSubmitHandler->handleFormSubmit($attachment->getData(), $attachment['file']->getData(), $options);
270
                } catch (AttachmentDownloadException $attachmentDownloadException) {
271
                    $this->addFlash(
272
                        'error',
273
                        $translator->trans('attachment.download_failed').' '.$attachmentDownloadException->getMessage()
274
                    );
275
                }
276
            }
277
278
            $this->commentHelper->setMessage($form['log_comment']->getData());
279
280
            $em->persist($new_part);
281
            $em->flush();
282
            $this->addFlash('success', 'part.created_flash');
283
284
            return $this->redirectToRoute('part_edit', ['id' => $new_part->getID()]);
285
        }
286
287
        if ($form->isSubmitted() && ! $form->isValid()) {
288
            $this->addFlash('error', 'part.created_flash.invalid');
289
        }
290
291
        return $this->render('Parts/edit/new_part.html.twig',
292
                             [
293
                                 'part' => $new_part,
294
                                 'form' => $form->createView(),
295
                                 'attachment_helper' => $attachmentHelper,
296
                             ]);
297
    }
298
}
299