Passed
Push — master ( 9fa2bb...0992fe )
by Michael
01:53
created

DefaultController::getParentHitch()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.2
c 0
b 0
f 0
ccs 12
cts 12
cp 1
cc 4
eloc 11
nc 4
nop 0
crap 4
1
<?php
2
/* Copyright (C) 2015-2017 Michael Giesler, Stephan Kreutzer
3
 *
4
 * This file is part of Dembelo.
5
 *
6
 * Dembelo is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * Dembelo is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
 * GNU Affero General Public License 3 for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public License 3
17
 * along with Dembelo. If not, see <http://www.gnu.org/licenses/>.
18
 */
19
20
declare(strict_types = 1);
21
22
namespace DembeloMain\Controller;
23
24
use DembeloMain\Document\Textnode;
25
use DembeloMain\Document\TextnodeHitch;
26
use DembeloMain\Document\User;
27
use DembeloMain\Model\FavoriteManager;
28
use DembeloMain\Model\FeatureToggle;
29
use DembeloMain\Model\Readpath;
30
use DembeloMain\Model\Repository\TextNodeRepositoryInterface;
31
use DembeloMain\Model\Repository\UserRepositoryInterface;
32
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
33
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
34
use Symfony\Component\HttpFoundation\RedirectResponse;
35
use Symfony\Component\HttpFoundation\Response;
36
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
37
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
38
use Symfony\Bundle\FrameworkBundle\Routing\Router;
39
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface as Templating;
40
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
41
42
/**
43
 * Class DefaultController
44
 * @Route(service="app.controller_default")
45
 */
46
class DefaultController extends Controller
47
{
48
    /**
49
     * @var FeatureToggle
50
     */
51
    private $featureToggle;
52
53
    /**
54
     * @var AuthorizationCheckerInterface
55
     */
56
    private $authorizationChecker;
57
58
    /**
59
     * @var UserRepositoryInterface
60
     */
61
    private $userRepository;
62
63
    /**
64
     * @var TextNodeRepositoryInterface
65
     */
66
    private $textnodeRepository;
67
68
    /**
69
     * @var Templating
70
     */
71
    private $templating;
72
73
    /**
74
     * @var Router
75
     */
76
    private $router;
77
78
    /**
79
     * @var TokenStorage
80
     */
81
    private $tokenStorage;
82
83
    /**
84
     * @var Readpath
85
     */
86
    private $readpath;
87
88
    /**
89
     * @var FavoriteManager
90
     */
91
    private $favoriteManager;
92
93
    /**
94
     * DefaultController constructor.
95
     * @param FeatureToggle                 $featureToggle
96
     * @param AuthorizationCheckerInterface $authorizationChecker
97
     * @param UserRepositoryInterface       $userRepository
98
     * @param TextNodeRepositoryInterface   $textNodeRepository
99
     * @param Templating                    $templating
100
     * @param Router                        $router
101
     * @param TokenStorage                  $tokenStorage
102
     * @param Readpath                      $readpath
103
     * @param FavoriteManager               $favoriteManager
104
     */
105 16
    public function __construct(FeatureToggle $featureToggle, AuthorizationCheckerInterface $authorizationChecker, UserRepositoryInterface $userRepository, TextNodeRepositoryInterface $textNodeRepository, Templating $templating, Router $router, TokenStorage $tokenStorage, Readpath $readpath, FavoriteManager $favoriteManager)
106
    {
107 16
        $this->featureToggle = $featureToggle;
108 16
        $this->authorizationChecker = $authorizationChecker;
109 16
        $this->userRepository = $userRepository;
110 16
        $this->textnodeRepository = $textNodeRepository;
111 16
        $this->templating = $templating;
112 16
        $this->router = $router;
113 16
        $this->tokenStorage = $tokenStorage;
114 16
        $this->readpath = $readpath;
115 16
        $this->favoriteManager = $favoriteManager;
116 16
    }
117
118
    /**
119
     * @Route("/themenfeld/{topicId}", name="themenfeld")
120
     *
121
     * @param string $topicId Topic ID from URL
122
     *
123
     * @return RedirectResponse
124
     *
125
     * @throws NotFoundHttpException
126
     */
127 5
    public function readTopicAction($topicId): RedirectResponse
128
    {
129 5
        if ($this->featureToggle->hasFeature('login_needed') && !$this->authorizationChecker->isGranted('ROLE_USER')) {
130 1
            return $this->redirectToRoute('login_route');
131
        }
132
133 4
        $textnode = $this->textnodeRepository->getTextnodeToRead($topicId);
134
135 4
        if (null === $textnode) {
136 1
            throw $this->createNotFoundException(sprintf('No Textnode for Topic \'%s\' found.', $topicId));
137
        }
138
139 3
        $user = $this->getUser();
140 3
        if ($user instanceof User) {
141 1
            $user->setLastTopicId($topicId);
142 1
            $this->userRepository->save($user);
143
        }
144
145 3
        if ($textnode->isFinanceNode()) {
146 1
            return $this->redirectToRoute('financenode', ['textnodeArbitraryId' => $textnode->getArbitraryId()]);
147
        }
148
149 2
        return $this->redirectToRoute('text', array('textnodeArbitraryId' => $textnode->getArbitraryId()));
150
    }
151
152
    /**
153
     * @Route("/text/{textnodeArbitraryId}", name="text")
154
     *
155
     * @param string $textnodeArbitraryId Textnode arbitrary ID from URL
156
     *
157
     * @return Response
158
     *
159
     * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
160
     */
161 4
    public function readTextnodeAction($textnodeArbitraryId): Response
162
    {
163 4
        if ($this->featureToggle->hasFeature('login_needed') && !$this->authorizationChecker->isGranted('ROLE_USER')) {
164 1
            return $this->redirectToRoute('login_route');
165
        }
166
167 3
        $textnode = $this->textnodeRepository->findOneActiveByArbitraryId($textnodeArbitraryId);
168
169 3
        if (null === $textnode) {
170 1
            throw $this->createNotFoundException(sprintf('No Textnode with arbitrary ID \'%s\' found.', $textnodeArbitraryId));
171
        }
172
173 2
        if ($textnode->isFinanceNode()) {
174 1
            return $this->redirectToRoute('financenode', ['textnodeArbitraryId' => $textnode->getArbitraryId()]);
175
        }
176
177 1
        $user = $this->getUser();
178
179 1
        $this->readpath->storeReadpath($textnode, $user);
180 1
        $this->favoriteManager->setFavorite($textnode, $user);
181
182 1
        if ($user instanceof User) {
183
            $this->userRepository->save($user);
184
        }
185
186 1
        $hitches = [];
187
188 1
        $childHitches = $textnode->getChildHitches();
189 1
        $index = 0;
190 1
        foreach ($childHitches as $childHitch) {
191
            $hitchedTextnode = $childHitch->getTargetTextnode();
192
            $hitches[] = [
193
                'index' => $index,
194
                'description' => $childHitch->getDescription(),
195
                'arbitraryId' => $hitchedTextnode->getArbitraryId(),
196
                'isFinanceNode' => $hitchedTextnode->isFinanceNode(),
197
            ];
198
            ++$index;
199
        }
200
201 1
        $showBackButton = $this->showBackButton($textnode);
202
203 1
        return $this->templating->renderResponse(
204 1
            'DembeloMain::default/read.html.twig',
205
            [
206 1
                'textnode' => $textnode,
207 1
                'hitches' => $hitches,
208 1
                'showBackButton' => $showBackButton,
209
            ]
210
        );
211
    }
212
213
    /**
214
     * @Route("/paywall/{textnodeId}/{hitchIndex}", name="paywall")
215
     *
216
     * @param string $textnodeId Textnode ID from URL
217
     * @param string $hitchIndex hitch index
218
     *
219
     * @return Response
220
     */
221
    public function paywallAction($textnodeId, $hitchIndex): Response
222
    {
223
        $hitchedTextnode = $this->getTextnodeForHitchIndex($textnodeId, $hitchIndex);
0 ignored issues
show
Bug introduced by
$hitchIndex of type string is incompatible with the type integer expected by parameter $hitchIndex of DembeloMain\Controller\D...TextnodeForHitchIndex(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

223
        $hitchedTextnode = $this->getTextnodeForHitchIndex($textnodeId, /** @scrutinizer ignore-type */ $hitchIndex);
Loading history...
224
225
        $url = $this->router->generate('text', ['textnodeArbitraryId' => $hitchedTextnode->getArbitraryId()]);
226
227
        $output = [
228
            'url' => $url,
229
        ];
230
231
        return new Response(\json_encode($output));
232
    }
233
234
    /**
235
     * @Route("/back", name="back")
236
     *
237
     * @return RedirectResponse
238
     */
239 6
    public function backAction(): RedirectResponse
240
    {
241 6
        $parentHitch = $this->getParentHitch();
242 6
        if (null === $parentHitch) {
243 4
            return $this->redirectToRoute('mainpage');
244
        }
245 2
        $parentTextnode = $parentHitch->getSourceTextnode();
246 2
        if ($parentTextnode->getAccess()) {
247 1
            return $this->redirectToRoute('themenfeld', ['topicId' => $parentTextnode->getTopicId()]);
248
        }
249
250 1
        return $this->redirectToRoute(
251 1
            'text',
252 1
            array('textnodeArbitraryId' => $parentTextnode->getArbitraryId())
253
        );
254
    }
255
256
    /**
257
     * @Route("/imprint", name="imprint")
258
     *
259
     * @return Response
260
     */
261 1
    public function imprintAction(): Response
262
    {
263 1
        return $this->templating->renderResponse('DembeloMain::default/imprint.html.twig');
264
    }
265
266
    /**
267
     * @param string $route
268
     * @param array  $parameters
269
     * @param int    $status
270
     *
271
     * @return RedirectResponse
272
     */
273 12
    protected function redirectToRoute($route, array $parameters = array(), $status = 302): RedirectResponse
274
    {
275 12
        $url = $this->router->generate($route, $parameters);
276
277 12
        return new RedirectResponse($url, $status);
278
    }
279
280
    /**
281
     * @return User|null
282
     */
283 10
    protected function getUser(): ?User
284
    {
285 10
        if (null === $token = $this->tokenStorage->getToken()) {
286 9
            return null;
287
        }
288
289 1
        if (!\is_object($user = $token->getUser())) {
290
            // e.g. anonymous authentication
291
            return null;
292
        }
293
294 1
        return $user;
295
    }
296
297
    /**
298
     * @param string $textnodeId
299
     * @param int    $hitchIndex
300
     *
301
     * @return Textnode|null
302
     *
303
     * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
304
     */
305
    private function getTextnodeForHitchIndex($textnodeId, $hitchIndex): ?Textnode
306
    {
307
        $textnode = $this->textnodeRepository->findOneActiveById($textnodeId);
308
309
        if (null === $textnode) {
310
            throw $this->createNotFoundException(sprintf('No Textnode with ID \'%s\' found.', $textnodeId));
311
        }
312
313
        /* @var $hitch TextnodeHitch */
314
        $hitch = $textnode->getChildHitches()->get($hitchIndex);
315
316
        return $hitch->getTargetTextnode();
317
    }
318
319
    /**
320
     * @param Textnode $textnode
321
     *
322
     * @return bool
323
     */
324 1
    private function showBackButton(Textnode $textnode): bool
325
    {
326 1
        if (false === $textnode->getAccess()) {
327
            return true;
328
        }
329
        $criteria = [
330 1
            'topic_id' => $textnode->getTopicId(),
331
        ];
332 1
        $accessNodes = $this->textnodeRepository->findBy($criteria);
333
334 1
        return (count($accessNodes) >= 2);
335
    }
336
337
    /**
338
     * @return TextnodeHitch|null
339
     */
340 6
    private function getParentHitch(): ?TextnodeHitch
341
    {
342 6
        $user = $this->getUser();
343 6
        $lastTextnodeId = $this->readpath->getCurrentTextnodeId($user);
344
345 6
        if (null === $lastTextnodeId) {
346 1
            return null;
347
        }
348 5
        $lastTextnode = $this->textnodeRepository->find($lastTextnodeId);
349 5
        if (null === $lastTextnode) {
350 1
            return null;
351
        }
352 4
        $parentHitches = $lastTextnode->getParentHitches();
353 4
        if ($parentHitches->isEmpty()) {
354 1
            return null;
355
        }
356
357 3
        return $parentHitches->first();
358
    }
359
}
360