Completed
Pull Request — 5.6 (#2830)
by Jeroen
14:14
created

NodeBundle/Helper/NodeAdmin/NodeAdminPublisher.php (6 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Kunstmaan\NodeBundle\Helper\NodeAdmin;
4
5
use Doctrine\ORM\EntityManager;
6
use Kunstmaan\AdminBundle\Entity\BaseUser;
7
use Kunstmaan\AdminBundle\FlashMessages\FlashTypes;
8
use Kunstmaan\AdminBundle\Helper\CloneHelper;
9
use Kunstmaan\AdminBundle\Helper\Security\Acl\Permission\PermissionMap;
10
use Kunstmaan\NodeBundle\Entity\HasNodeInterface;
11
use Kunstmaan\NodeBundle\Entity\Node;
12
use Kunstmaan\NodeBundle\Entity\NodeTranslation;
13
use Kunstmaan\NodeBundle\Entity\NodeVersion;
14
use Kunstmaan\NodeBundle\Entity\QueuedNodeTranslationAction;
15
use Kunstmaan\NodeBundle\Event\Events;
16
use Kunstmaan\NodeBundle\Event\NodeEvent;
17
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
18
use Symfony\Component\HttpFoundation\Request;
19
use Symfony\Component\HttpFoundation\Session\Session;
20
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
21
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
22
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
23
use Symfony\Component\Translation\TranslatorInterface;
24
25
class NodeAdminPublisher
26
{
27
    /**
28
     * @var EntityManager
29
     */
30
    private $em;
31
32
    /**
33
     * @var TokenStorageInterface
34
     */
35
    private $tokenStorage;
36
37
    /**
38
     * @var AuthorizationCheckerInterface
39
     */
40
    private $authorizationChecker;
41
42
    /**
43
     * @var EventDispatcherInterface
44
     */
45
    private $eventDispatcher;
46
47
    /**
48
     * @var CloneHelper
49
     */
50
    private $cloneHelper;
51
52
    /**
53
     * @param EntityManager                 $em                   The entity manager
54
     * @param TokenStorageInterface         $tokenStorage         The security token storage
55
     * @param AuthorizationCheckerInterface $authorizationChecker The security authorization checker
56
     * @param EventDispatcherInterface      $eventDispatcher      The Event dispatcher
57
     * @param CloneHelper                   $cloneHelper          The clone helper
58
     */
59 View Code Duplication
    public function __construct(
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
60
        EntityManager $em,
0 ignored issues
show
You have injected the EntityManager via parameter $em. This is generally not recommended as it might get closed and become unusable. Instead, it is recommended to inject the ManagerRegistry and retrieve the EntityManager via getManager() each time you need it.

The EntityManager might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:

function someFunction(ManagerRegistry $registry) {
    $em = $registry->getManager();
    $em->getConnection()->beginTransaction();
    try {
        // Do something.
        $em->getConnection()->commit();
    } catch (\Exception $ex) {
        $em->getConnection()->rollback();
        $em->close();

        throw $ex;
    }
}

If that code throws an exception and the EntityManager is closed. Any other code which depends on the same instance of the EntityManager during this request will fail.

On the other hand, if you instead inject the ManagerRegistry, the getManager() method guarantees that you will always get a usable manager instance.

Loading history...
61
        TokenStorageInterface $tokenStorage,
62
        AuthorizationCheckerInterface $authorizationChecker,
63
        EventDispatcherInterface $eventDispatcher,
64
        CloneHelper $cloneHelper
65
    ) {
66
        $this->em = $em;
67
        $this->tokenStorage = $tokenStorage;
68
        $this->authorizationChecker = $authorizationChecker;
69
        $this->eventDispatcher = $eventDispatcher;
70
        $this->cloneHelper = $cloneHelper;
71
    }
72
73
    /**
74
     * If there is a draft version it'll try to publish the draft first. Makse snese because if you want to publish the public version you don't publish but you save.
75
     *
76
     * @param BaseUser|null $user
77
     *
78
     *  @throws AccessDeniedException
79
     */
80
    public function publish(NodeTranslation $nodeTranslation, $user = null)
81
    {
82
        if (false === $this->authorizationChecker->isGranted(PermissionMap::PERMISSION_PUBLISH, $nodeTranslation->getNode())) {
83
            throw new AccessDeniedException();
84
        }
85
86
        if (\is_null($user)) {
87
            $user = $this->tokenStorage->getToken()->getUser();
88
        }
89
90
        $node = $nodeTranslation->getNode();
91
92
        $nodeVersion = $nodeTranslation->getNodeVersion('draft');
93
        if (!\is_null($nodeVersion)) {
94
            $page = $nodeVersion->getRef($this->em);
95
            /** @var NodeVersion $nodeVersion */
96
            $nodeVersion = $this->createPublicVersion($page, $nodeTranslation, $nodeVersion, $user);
0 ignored issues
show
It seems like $page defined by $nodeVersion->getRef($this->em) on line 94 can be null; however, Kunstmaan\NodeBundle\Hel...::createPublicVersion() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
97
            $nodeTranslation = $nodeVersion->getNodeTranslation();
98
        } else {
99
            $nodeVersion = $nodeTranslation->getPublicNodeVersion();
100
        }
101
102
        $page = $nodeVersion->getRef($this->em);
103
104
        $this->eventDispatcher->dispatch(
105
            Events::PRE_PUBLISH,
106
            new NodeEvent($node, $nodeTranslation, $nodeVersion, $page)
0 ignored issues
show
It seems like $page defined by $nodeVersion->getRef($this->em) on line 102 can be null; however, Kunstmaan\NodeBundle\Eve...odeEvent::__construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
107
        );
108
        $nodeTranslation
109
            ->setOnline(true)
110
            ->setPublicNodeVersion($nodeVersion)
111
            ->setUpdated(new \DateTime());
112
        $this->em->persist($nodeTranslation);
113
        $this->em->flush();
114
115
        // Remove scheduled task
116
        $this->unSchedulePublish($nodeTranslation);
117
118
        $this->eventDispatcher->dispatch(
119
            Events::POST_PUBLISH,
120
            new NodeEvent($node, $nodeTranslation, $nodeVersion, $page)
0 ignored issues
show
It seems like $page defined by $nodeVersion->getRef($this->em) on line 102 can be null; however, Kunstmaan\NodeBundle\Eve...odeEvent::__construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
121
        );
122
    }
123
124
    /**
125
     * @param NodeTranslation $nodeTranslation The NodeTranslation
126
     * @param \DateTime       $date            The date to publish
127
     *
128
     * @throws AccessDeniedException
129
     */
130 View Code Duplication
    public function publishLater(NodeTranslation $nodeTranslation, \DateTime $date)
131
    {
132
        $node = $nodeTranslation->getNode();
133
        if (false === $this->authorizationChecker->isGranted(PermissionMap::PERMISSION_PUBLISH, $node)) {
134
            throw new AccessDeniedException();
135
        }
136
137
        //remove existing first
138
        $this->unSchedulePublish($nodeTranslation);
139
140
        $user = $this->tokenStorage->getToken()->getUser();
141
        $queuedNodeTranslationAction = new QueuedNodeTranslationAction();
142
        $queuedNodeTranslationAction
143
            ->setNodeTranslation($nodeTranslation)
144
            ->setAction(QueuedNodeTranslationAction::ACTION_PUBLISH)
145
            ->setUser($user)
146
            ->setDate($date);
147
        $this->em->persist($queuedNodeTranslationAction);
148
        $this->em->flush();
149
    }
150
151
    /**
152
     * @throws AccessDeniedException
153
     */
154
    public function unPublish(NodeTranslation $nodeTranslation)
155
    {
156
        if (false === $this->authorizationChecker->isGranted(PermissionMap::PERMISSION_UNPUBLISH, $nodeTranslation->getNode())) {
157
            throw new AccessDeniedException();
158
        }
159
160
        $node = $nodeTranslation->getNode();
161
        $nodeVersion = $nodeTranslation->getPublicNodeVersion();
162
        $page = $nodeVersion->getRef($this->em);
163
164
        $this->eventDispatcher->dispatch(
165
            Events::PRE_UNPUBLISH,
166
            new NodeEvent($node, $nodeTranslation, $nodeVersion, $page)
0 ignored issues
show
It seems like $page defined by $nodeVersion->getRef($this->em) on line 162 can be null; however, Kunstmaan\NodeBundle\Eve...odeEvent::__construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
167
        );
168
        $nodeTranslation->setOnline(false);
169
        $this->em->persist($nodeTranslation);
170
        $this->em->flush();
171
172
        // Remove scheduled task
173
        $this->unSchedulePublish($nodeTranslation);
174
175
        $this->eventDispatcher->dispatch(
176
            Events::POST_UNPUBLISH,
177
            new NodeEvent($node, $nodeTranslation, $nodeVersion, $page)
178
        );
179
    }
180
181
    /**
182
     * @param NodeTranslation $nodeTranslation The NodeTranslation
183
     * @param \DateTime       $date            The date to unpublish
184
     *
185
     * @throws AccessDeniedException
186
     */
187 View Code Duplication
    public function unPublishLater(NodeTranslation $nodeTranslation, \DateTime $date)
188
    {
189
        $node = $nodeTranslation->getNode();
190
        if (false === $this->authorizationChecker->isGranted(PermissionMap::PERMISSION_UNPUBLISH, $node)) {
191
            throw new AccessDeniedException();
192
        }
193
194
        //remove existing first
195
        $this->unSchedulePublish($nodeTranslation);
196
        $user = $this->tokenStorage->getToken()->getUser();
197
        $queuedNodeTranslationAction = new QueuedNodeTranslationAction();
198
        $queuedNodeTranslationAction
199
            ->setNodeTranslation($nodeTranslation)
200
            ->setAction(QueuedNodeTranslationAction::ACTION_UNPUBLISH)
201
            ->setUser($user)
202
            ->setDate($date);
203
        $this->em->persist($queuedNodeTranslationAction);
204
        $this->em->flush();
205
    }
206
207
    public function unSchedulePublish(NodeTranslation $nodeTranslation)
208
    {
209
        /* @var Node $node */
210
        $queuedNodeTranslationAction = $this->em->getRepository(QueuedNodeTranslationAction::class)
211
            ->findOneBy(['nodeTranslation' => $nodeTranslation]);
212
213
        if (!\is_null($queuedNodeTranslationAction)) {
214
            $this->em->remove($queuedNodeTranslationAction);
215
            $this->em->flush();
216
        }
217
    }
218
219
    /**
220
     * This shouldn't be here either but it's an improvement.
221
     *
222
     * @param HasNodeInterface $page            The page
223
     * @param NodeTranslation  $nodeTranslation The node translation
224
     * @param NodeVersion      $nodeVersion     The node version
225
     * @param BaseUser         $user            The user
226
     *
227
     * @return mixed
228
     */
229
    public function createPublicVersion(
230
        HasNodeInterface $page,
231
        NodeTranslation $nodeTranslation,
232
        NodeVersion $nodeVersion,
233
        BaseUser $user
234
    ) {
235
        $newPublicPage = $this->cloneHelper->deepCloneAndSave($page);
236
        $newNodeVersion = $this->em->getRepository(NodeVersion::class)->createNodeVersionFor(
237
            $newPublicPage,
238
            $nodeTranslation,
239
            $user,
240
            $nodeVersion
241
        );
242
243
        $newNodeVersion
244
            ->setOwner($nodeVersion->getOwner())
245
            ->setUpdated($nodeVersion->getUpdated())
246
            ->setCreated($nodeVersion->getCreated());
247
248
        $nodeVersion
249
            ->setOwner($user)
250
            ->setCreated(new \DateTime())
251
            ->setOrigin($newNodeVersion);
252
253
        $this->em->persist($newNodeVersion);
254
        $this->em->persist($nodeVersion);
255
        $this->em->persist($nodeTranslation);
256
        $this->em->flush();
257
        $this->eventDispatcher->dispatch(
258
            Events::CREATE_PUBLIC_VERSION,
259
            new NodeEvent($nodeTranslation->getNode(), $nodeTranslation, $nodeVersion, $newPublicPage)
260
        );
261
262
        return $newNodeVersion;
263
    }
264
265 View Code Duplication
    public function chooseHowToPublish(Request $request, NodeTranslation $nodeTranslation, TranslatorInterface $translator)
266
    {
267
        /** @var Session $session */
268
        $session = $request->getSession();
269
270
        if ($request->request->has('publish_later') && $request->get('pub_date')) {
271
            $date = new \DateTime(
272
                $request->get('pub_date') . ' ' . $request->get('pub_time')
273
            );
274
            $this->publishLater($nodeTranslation, $date);
275
            $session->getFlashBag()->add(
276
                FlashTypes::SUCCESS,
277
                $translator->trans('kuma_node.admin.publish.flash.success_scheduled')
278
            );
279
        } else {
280
            $this->publish($nodeTranslation);
281
            $session->getFlashBag()->add(
282
                FlashTypes::SUCCESS,
283
                $translator->trans('kuma_node.admin.publish.flash.success_published')
284
            );
285
        }
286
    }
287
288 View Code Duplication
    public function chooseHowToUnpublish(Request $request, NodeTranslation $nodeTranslation, TranslatorInterface $translator)
289
    {
290
        /** @var Session $session */
291
        $session = $request->getSession();
292
293
        if ($request->request->has('unpublish_later') && $request->get('unpub_date')) {
294
            $date = new \DateTime($request->get('unpub_date') . ' ' . $request->get('unpub_time'));
295
            $this->unPublishLater($nodeTranslation, $date);
296
            $session->getFlashBag()->add(
297
                FlashTypes::SUCCESS,
298
                $translator->trans('kuma_node.admin.unpublish.flash.success_scheduled')
299
            );
300
        } else {
301
            $this->unPublish($nodeTranslation);
302
            $session->getFlashBag()->add(
303
                FlashTypes::SUCCESS,
304
                $translator->trans('kuma_node.admin.unpublish.flash.success_unpublished')
305
            );
306
        }
307
    }
308
}
309