Completed
Push — 1.4 ( eba84d...d3bf7f )
by Rafał
08:33
created

PackagePreviewController::getArticleForPreview()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
c 0
b 0
f 0
rs 9.7998
cc 1
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Superdesk Web Publisher Content Bundle.
7
 *
8
 * Copyright 2017 Sourcefabric z.ú. and contributors.
9
 *
10
 * For the full copyright and license information, please see the
11
 * AUTHORS and LICENSE files distributed with this source code.
12
 *
13
 * @copyright 2017 Sourcefabric z.ú
14
 * @license http://www.superdesk.org/license
15
 */
16
17
namespace SWP\Bundle\CoreBundle\Controller;
18
19
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
20
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
21
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
22
use SWP\Bundle\ContentBundle\ArticleEvents;
23
use SWP\Bundle\ContentBundle\Model\RouteInterface;
24
use SWP\Bundle\CoreBundle\Context\ArticlePreviewContext;
25
use SWP\Bundle\CoreBundle\Model\ArticleInterface;
26
use SWP\Bundle\CoreBundle\Model\ArticlePreview;
27
use SWP\Bundle\CoreBundle\Model\PackageInterface;
28
use SWP\Bundle\CoreBundle\Model\PackagePreviewTokenInterface;
29
use SWP\Bundle\CoreBundle\Service\ArticlePreviewer;
30
use SWP\Component\Bridge\Events;
31
use SWP\Component\Common\Response\SingleResourceResponse;
32
use SWP\Component\Common\Response\SingleResourceResponseInterface;
33
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
34
use Symfony\Component\EventDispatcher\GenericEvent;
35
use Symfony\Component\HttpFoundation\RedirectResponse;
36
use Symfony\Component\HttpFoundation\Request;
37
use Symfony\Component\HttpFoundation\Response;
38
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
39
40
class PackagePreviewController extends Controller
41
{
42
    /**
43
     * @Route("/preview/package/{routeId}/{id}", options={"expose"=true}, requirements={"id"="\d+", "routeId"="\d+", "token"=".+"}, name="swp_package_preview")
44
     * @Method("GET")
45
     */
46
    public function previewAction(int $routeId, $id)
47
    {
48
        /** @var RouteInterface $route */
49
        $route = $this->findRouteOr404($routeId);
50
        /** @var PackageInterface $package */
51
        $package = $this->findPackageOr404($id);
52
        $articlePreviewer = $this->get(ArticlePreviewer::class);
53
        $article = $articlePreviewer->preview($package, $route);
54
55
        $articlePreview = new ArticlePreview();
56
        $articlePreview->setArticle($article);
57
58
        $this->get('event_dispatcher')->dispatch(ArticleEvents::PREVIEW, new GenericEvent($articlePreview));
59
60
        if (null !== ($url = $articlePreview->getPreviewUrl())) {
61
            return new RedirectResponse($url);
62
        }
63
64
        $route = $this->ensureRouteTemplateExists($route, $article);
65
66
        try {
67
            return $this->render($route->getArticlesTemplateName());
68
        } catch (\Exception $e) {
69
            throw $this->createNotFoundException(
70
                sprintf('Template for route with id "%d" (%s) not found!', $route->getId(), $route->getName())
71
            );
72
        }
73
    }
74
75
    /**
76
     * Generates package preview token for specific route.
77
     *
78
     * @ApiDoc(
79
     *     resource=true,
80
     *     description="Generate package preview token for specific route",
81
     *     statusCodes={
82
     *         200="Returned on success.",
83
     *         400="Returned when validation failed.",
84
     *         500="Returned when unexpected error."
85
     *     }
86
     * )
87
     * @Route("/api/{version}/preview/package/generate_token/{routeId}", options={"expose"=true}, defaults={"version"="v1"}, name="swp_api_core_preview_package_token", requirements={"routeId"="\d+"})
88
     * @Method("POST")
89
     */
90
    public function generateTokenAction(Request $request, int $routeId)
91
    {
92
        $route = $this->findRouteOr404($routeId);
93
94
        /** @var string $content */
95
        $content = (string) $request->getContent();
96
        $dispatcher = $this->get('event_dispatcher');
97
        $package = $this->get('swp_bridge.transformer.json_to_package')->transform($content);
98
        $dispatcher->dispatch(Events::SWP_VALIDATION, new GenericEvent($package));
99
100
        $tokenRepository = $this->get('swp.repository.package_preview_token');
101
        $existingPreviewToken = $tokenRepository->findOneBy(['route' => $route]);
102
103
        if (null === $existingPreviewToken) {
104
            $packagePreviewToken = $this->get('swp.factory.package_preview_token')->createTokenizedWith($route, $content);
105
106
            $tokenRepository->persist($packagePreviewToken);
107
            $tokenRepository->flush();
108
109
            return $this->returnResponseWithPreviewUrl($packagePreviewToken);
110
        }
111
112
        $this->updatePackagePreviewTokenBody($content, $existingPreviewToken);
113
114
        return $this->returnResponseWithPreviewUrl($existingPreviewToken);
115
    }
116
117
    private function updatePackagePreviewTokenBody(string $content, PackagePreviewTokenInterface $packagePreviewToken)
118
    {
119
        if (md5($content) !== md5($packagePreviewToken->getBody())) {
120
            $packagePreviewToken->setBody($content);
121
122
            $this->get('swp.repository.package_preview_token')->flush();
123
        }
124
    }
125
126
    private function returnResponseWithPreviewUrl(PackagePreviewTokenInterface $packagePreviewToken): SingleResourceResponseInterface
127
    {
128
        $article = $this->getArticleForPreview($packagePreviewToken);
129
        $articlePreview = new ArticlePreview();
130
        $articlePreview->setArticle($article);
131
132
        $this->get('event_dispatcher')->dispatch(ArticleEvents::PREVIEW, new GenericEvent($articlePreview));
133
134
        $url = $articlePreview->getPreviewUrl();
135
136
        if (null === $url) {
137
            $url = $this->generateUrl(
138
                'swp_package_preview_publish',
139
                ['token' => $packagePreviewToken->getToken()],
140
                UrlGeneratorInterface::ABSOLUTE_URL
141
            );
142
        }
143
144
        return new SingleResourceResponse([
145
            'preview_url' => $url,
146
        ]);
147
    }
148
149
    /**
150
     * @Route("/preview/publish/package/{token}", options={"expose"=true}, requirements={"token"=".+"}, name="swp_package_preview_publish")
151
     * @Method("GET")
152
     */
153
    public function publishPreviewAction(string $token)
154
    {
155
        $existingPreviewToken = $this->get('swp.repository.package_preview_token')->findOneBy(['token' => $token]);
156
157
        if (null === $existingPreviewToken) {
158
            throw $this->createNotFoundException(sprintf('Token %s is not valid.', $token));
159
        }
160
161
        $article = $this->getArticleForPreview($existingPreviewToken);
162
        $route = $article->getRoute();
163
        $route = $this->ensureRouteTemplateExists($route, $article);
164
165
        return $this->renderTemplateOr404($route);
166
    }
167
168
    private function getArticleForPreview(PackagePreviewTokenInterface $packagePreviewToken): ArticleInterface
169
    {
170
        $dispatcher = $this->get('event_dispatcher');
171
        $package = $this->get('swp_bridge.transformer.json_to_package')->transform($packagePreviewToken->getBody());
172
        $dispatcher->dispatch(Events::SWP_VALIDATION, new GenericEvent($package));
173
174
        $articlePreviewer = $this->get(ArticlePreviewer::class);
175
        $articlePreviewContext = $this->get(ArticlePreviewContext::class);
176
177
        $articlePreviewContext->setIsPreview(true);
178
        $article = $articlePreviewer->preview($package, $packagePreviewToken->getRoute());
179
180
        return $article;
181
    }
182
183
    private function renderTemplateOr404(RouteInterface $route): Response
184
    {
185
        try {
186
            return $this->render($templateName = $route->getArticlesTemplateName());
187
        } catch (\InvalidArgumentException $e) {
188
            throw $this->createNotFoundException(
189
                sprintf('Template %s for route with id "%d" (%s) not found!', $templateName, $route->getId(), $route->getName())
190
            );
191
        }
192
    }
193
194
    private function ensureRouteTemplateExists(RouteInterface $route, ArticleInterface $article): RouteInterface
195
    {
196
        if (null === $route->getArticlesTemplateName()) {
197
            $templateNameResolver = $this->get('swp_core.theme.resolver.template_name');
198
            $route->setArticlesTemplateName($templateNameResolver->resolve($article));
199
        }
200
201
        return $route;
202
    }
203
204
    /**
205
     * @param int $id
206
     *
207
     * @return null|object
208
     */
209
    private function findRouteOr404(int $id)
210
    {
211
        if (null === ($route = $this->get('swp.repository.route')->findOneBy(['id' => $id]))) {
212
            throw $this->createNotFoundException(sprintf('Route with id: "%s" not found!', $id));
213
        }
214
215
        return $route;
216
    }
217
218
    /**
219
     * @param string $id
220
     *
221
     * @return null|object
222
     */
223
    private function findPackageOr404(string $id)
224
    {
225
        if (null === ($package = $this->get('swp.repository.package')->findOneBy(['id' => $id]))) {
226
            throw $this->createNotFoundException(sprintf('Package with id: "%s" not found!', $id));
227
        }
228
229
        return $package;
230
    }
231
}
232