Issues (3099)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

Repository/NodeTranslationRepository.php (2 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\Repository;
4
5
use Doctrine\ORM\EntityRepository;
6
use Doctrine\ORM\Query\ResultSetMappingBuilder;
7
use Doctrine\ORM\QueryBuilder;
8
use Kunstmaan\AdminBundle\Entity\BaseUser;
9
use Kunstmaan\NodeBundle\Entity\HasNodeInterface;
10
use Kunstmaan\NodeBundle\Entity\Node;
11
use Kunstmaan\NodeBundle\Entity\NodeTranslation;
12
use Kunstmaan\NodeBundle\Entity\NodeVersion;
13
use Kunstmaan\UtilitiesBundle\Helper\ClassLookup;
14
15
/**
16
 * NodeRepository
17
 */
18
class NodeTranslationRepository extends EntityRepository
19
{
20
    /**
21
     * Get the QueryBuilder based on node id and language.
22
     *
23
     * @deprecated This method is deprecated since KunstmaanNodeBundle 5.7 and will be removed in KunstmaanNodeBundle 6.0. Use the renamed method "getNodeTranslationByNodeId" instead.
24
     *
25
     * @param int    $nodeId
26
     * @param string $lang
27
     *
28
     * @return NodeTranslation|null
29
     */
30
    public function getNodeTranslationByNodeIdQueryBuilder($nodeId, $lang)
31
    {
32
        @trigger_error(sprintf('The method "%s" is deprecated since KunstmaanNodeBundle 5.7 and will be removed in KunstmaanNodeBundle 6.0. Use the renamed method "getNodeTranslationByNodeId" instead.', __METHOD__), E_USER_DEPRECATED);
33
34
        return $this->getNodeTranslationByNodeId($nodeId, $lang);
35
    }
36
37
    /**
38
     * NEXT_MAJOR: add int typehint for $nodeId.
39
     *
40
     * @param int $nodeId
41
     *
42
     * @return NodeTranslation|null
43
     */
44
    public function getNodeTranslationByNodeId($nodeId, string $lang)
45
    {
46
        if (!is_int($nodeId)) {
47
            @trigger_error(sprintf('Not passing an integer for the "$nodeId" parameter in "%s" is deprecated since KunstmaanAdminBundle 5.7 and an integer typehint will be added in KunstmaanAdminBundle 6.0."', __METHOD__), E_USER_DEPRECATED);
48
        }
49
50
        $qb = $this->createQueryBuilder('nt')
51
            ->select('nt')
52
            ->innerJoin('nt.node', 'n', 'WITH', 'nt.node = n.id')
53
            ->where('n.deleted != 1')
54
            ->andWhere('nt.online = 1')
55
            ->andWhere('nt.lang = :lang')
56
            ->setParameter('lang', $lang)
57
            ->andWhere('n.id = :node_id')
58
            ->setParameter('node_id', $nodeId)
59
            ->setFirstResult(0)
60
            ->setMaxResults(1);
61
62
        return $qb->getQuery()->getOneOrNullResult();
63
    }
64
65
    /**
66
     * Get max children weight
67
     *
68
     * @param Node   $parentNode
0 ignored issues
show
Should the type for parameter $parentNode not be null|Node?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
69
     * @param string $lang       (optional) Only return max weight for the
0 ignored issues
show
Should the type for parameter $lang not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
70
     *                           given language
71
     *
72
     * @return int
73
     */
74
    public function getMaxChildrenWeight(Node $parentNode = null, $lang = null)
75
    {
76
        $maxWeight = $this->getNodeTranslationsQueryBuilder($lang)
77
            ->select('max(nt.weight)')
78
            ->andWhere('n.parent = :parentNode')
79
            ->setParameter('parentNode', $parentNode)
80
            ->getQuery()
81
            ->getSingleScalarResult();
82
83
        return (int) $maxWeight;
84
    }
85
86
    /**
87
     * QueryBuilder to fetch node translations (ignoring nodes that have been
88
     * deleted)
89
     *
90
     * @param string $lang (optional) Only return NodeTranslations for the
91
     *                     given language
92
     *
93
     * @return \Doctrine\ORM\QueryBuilder
94
     */
95
    public function getNodeTranslationsQueryBuilder($lang = null)
96
    {
97
        $queryBuilder = $this->createQueryBuilder('nt')
98
            ->select('nt,n,v')
99
            ->innerJoin('nt.node', 'n')
100
            ->leftJoin(
101
                'nt.publicNodeVersion',
102
                'v',
103
                'WITH',
104
                'nt.publicNodeVersion = v.id'
105
            )
106
            ->where('n.deleted = false')
107
            ->orderBy('nt.weight')
108
        ;
109
110
        if (!empty($lang)) {
111
            $queryBuilder
112
                ->andWhere('nt.lang = :lang')
113
                ->setParameter('lang', $lang);
114
        }
115
116
        return $queryBuilder;
117
    }
118
119
    /**
120
     * QueryBuilder to fetch node translations that are currently published
121
     * (ignoring nodes that have been deleted)
122
     *
123
     * @param string $lang (optional) Only return NodeTranslations for the
124
     *                     given language
125
     *
126
     * @return \Doctrine\ORM\QueryBuilder
127
     */
128
    public function getOnlineNodeTranslationsQueryBuilder($lang = null)
129
    {
130
        return $this->getNodeTranslationsQueryBuilder($lang)
131
            ->andWhere('nt.online = true');
132
    }
133
134
    /**
135
     * QueryBuilder to fetch immediate child NodeTranslations for a specific
136
     * node and (optional) language
137
     *
138
     * @return \Doctrine\ORM\QueryBuilder
139
     */
140
    public function getChildrenQueryBuilder(Node $parent, $lang = null)
141
    {
142
        return $this->getNodeTranslationsQueryBuilder($lang)
143
            ->andWhere('n.parent = :parent')
144
            ->setParameter('parent', $parent);
145
    }
146
147
    /**
148
     * QueryBuilder to fetch immediate child NodeTranslations for a specific
149
     * node and (optional) language that are currently published
150
     *
151
     * @return \Doctrine\ORM\QueryBuilder
152
     */
153
    public function getOnlineChildrenQueryBuilder(Node $parent, $lang = null)
154
    {
155
        return $this->getChildrenQueryBuilder($parent, $lang)
156
            ->andWhere('nt.online = true');
157
    }
158
159
    /**
160
     * Get all online child node translations for a given node and (optional)
161
     * language
162
     *
163
     * @param Node   $parent
164
     * @param string $lang   (optional, if not specified all languages will be
165
     *                       returned)
166
     *
167
     * @return array
168
     */
169
    public function getOnlineChildren(Node $parent, $lang = null)
170
    {
171
        return $this->getOnlineChildrenQueryBuilder($parent, $lang)
172
            ->getQuery()->getResult();
173
    }
174
175
    /**
176
     * Finds all nodetranslations where title is like the given $title parameter
177
     *
178
     * @param string $title
179
     * @param string $lang  (optional, if not specified all languages will be
180
     *                      returned)
181
     *
182
     * @return array
183
     */
184
    public function getNodeTranslationsLikeTitle($title, $lang = null)
185
    {
186
        /** @var QueryBuilder $qb */
187
        $qb = $this->getNodeTranslationsQueryBuilder($lang);
188
        $qb->andWhere('nt.title like :title')
189
            ->setParameter('title', '%' . $title . '%');
190
191
        return $qb->getQuery()->getResult();
192
    }
193
194
    /**
195
     * Get the node translation for a node
196
     *
197
     * @param HasNodeInterface $hasNode
198
     *
199
     * @return NodeTranslation
200
     */
201
    public function getNodeTranslationFor(HasNodeInterface $hasNode)
202
    {
203
        /* @var NodeVersion $nodeVersion */
204
        $nodeVersion = $this->getEntityManager()
205
            ->getRepository(NodeVersion::class)
206
            ->getNodeVersionFor($hasNode);
207
208
        if (!\is_null($nodeVersion)) {
209
            return $nodeVersion->getNodeTranslation();
210
        }
211
212
        return null;
213
    }
214
215
    /**
216
     * Get the node translation for a given slug string
217
     *
218
     * @param string               $slug       The slug
219
     * @param NodeTranslation|null $parentNode The parentnode
220
     *
221
     * @return NodeTranslation|null
222
     */
223
    public function getNodeTranslationForSlug(
224
        $slug,
225
        NodeTranslation $parentNode = null
226
    ) {
227
        if (empty($slug)) {
228
            return $this->getNodeTranslationForSlugPart(null, $slug);
229
        }
230
231
        $slugParts = explode('/', $slug);
232
        $result = $parentNode;
233
        foreach ($slugParts as $slugPart) {
234
            $result = $this->getNodeTranslationForSlugPart($result, $slugPart);
235
        }
236
237
        return $result;
238
    }
239
240
    /**
241
     * Returns the node translation for a given slug
242
     *
243
     * @param NodeTranslation|null $parentNode The parentNode
244
     * @param string               $slugPart   The slug part
245
     *
246
     * @return NodeTranslation|null
247
     */
248
    private function getNodeTranslationForSlugPart(
249
        NodeTranslation $parentNode = null,
250
        $slugPart = ''
251
    ) {
252
        $qb = $this->createQueryBuilder('t')
253
            ->select('t', 'v', 'n')
254
            ->innerJoin('t.node', 'n', 'WITH', 't.node = n.id')
255
            ->leftJoin(
256
                't.publicNodeVersion',
257
                'v',
258
                'WITH',
259
                't.publicNodeVersion = v.id'
260
            )
261
            ->where('n.deleted != 1')
262
            ->setFirstResult(0)
263
            ->setMaxResults(1);
264
265
        if ($parentNode !== null) {
266
            $qb->andWhere('t.slug = :slug')
267
                ->andWhere('n.parent = :parent')
268
                ->setParameter('slug', $slugPart)
269
                ->setParameter('parent', $parentNode->getNode()->getId());
270
        } else {
271
            /* if parent is null we should look for slugs that have no parent */
272
            $qb->andWhere('n.parent IS NULL');
273
            if (empty($slugPart)) {
274
                $qb->andWhere('t.slug is NULL');
275
            } else {
276
                $qb->andWhere('t.slug = :slug');
277
                $qb->setParameter('slug', $slugPart);
278
            }
279
        }
280
281
        return $qb->getQuery()->getOneOrNullResult();
282
    }
283
284
    /**
285
     * Get the node translation for a given url
286
     *
287
     * @param string          $urlSlug        The full url
288
     * @param string          $locale         The locale
289
     * @param bool            $includeDeleted Include deleted nodes
290
     * @param NodeTranslation $toExclude      Optional NodeTranslation instance
291
     *                                        you wish to exclude
292
     * @param Node            $rootNode       Optional Root node of the tree you
293
     *                                        wish to use
294
     *
295
     * @return array
296
     */
297
    public function getAllNodeTranslationsForUrl(
298
        $urlSlug,
299
        $locale = '',
300
        $includeDeleted = false,
301
        NodeTranslation $toExclude = null,
302
        Node $rootNode = null
303
    ) {
304
        $qb = $this->createQueryBuilder('b')
305
            ->select('b', 'v')
306
            ->innerJoin('b.node', 'n', 'WITH', 'b.node = n.id')
307
            ->leftJoin(
308
                'b.publicNodeVersion',
309
                'v',
310
                'WITH',
311
                'b.publicNodeVersion = v.id'
312
            )
313
            ->addOrderBy('b.online', 'DESC')
314
            ->setFirstResult(0)
315
            ->setMaxResults(1);
316
317
        if (!$includeDeleted) {
318
            $qb->andWhere('n.deleted = 0');
319
        }
320
321
        if (!empty($locale)) {
322
            $qb->andWhere('b.lang = :lang')
323
                ->setParameter('lang', $locale);
324
        }
325
326
        if (empty($urlSlug)) {
327
            $qb->andWhere('b.url IS NULL');
328
        } else {
329
            $qb->andWhere('b.url = :url');
330
            $qb->setParameter('url', $urlSlug);
331
        }
332
333
        if (!\is_null($toExclude)) {
334
            $qb->andWhere('NOT b.id = :exclude_id')
335
                ->setParameter('exclude_id', $toExclude->getId());
336
        }
337
338
        if ($rootNode) {
339
            $qb->andWhere('n.lft >= :left')
340
                ->andWhere('n.rgt <= :right')
341
                ->setParameter('left', $rootNode->getLeft())
342
                ->setParameter('right', $rootNode->getRight());
343
        }
344
345
        return $qb->getQuery()->getResult();
346
    }
347
348
    /**
349
     * Get the node translation for a given url
350
     *
351
     * @param string          $urlSlug        The full url
352
     * @param string          $locale         The locale
353
     * @param bool            $includeDeleted Include deleted nodes
354
     * @param NodeTranslation $toExclude      Optional NodeTranslation instance
355
     *                                        you wish to exclude
356
     * @param Node            $rootNode       Optional Root node of the tree you
357
     *                                        wish to use
358
     *
359
     * @return NodeTranslation|null
360
     */
361
    public function getNodeTranslationForUrl(
362
        $urlSlug,
363
        $locale = '',
364
        $includeDeleted = false,
365
        NodeTranslation $toExclude = null,
366
        Node $rootNode = null
367
    ) {
368
        $translations = $this->getAllNodeTranslationsForUrl($urlSlug, $locale, $includeDeleted, $toExclude, $rootNode);
369
370
        if (empty($translations)) {
371
            return null;
372
        }
373
374
        return $translations[0];
375
    }
376
377
    /**
378
     * Get all top node translations
379
     *
380
     * @return NodeTranslation[]
381
     */
382
    public function getTopNodeTranslations()
383
    {
384
        $qb = $this->createQueryBuilder('b')
385
            ->select('b', 'v')
386
            ->innerJoin('b.node', 'n', 'WITH', 'b.node = n.id')
387
            ->leftJoin(
388
                'b.publicNodeVersion',
389
                'v',
390
                'WITH',
391
                'b.publicNodeVersion = v.id'
392
            )
393
            ->where('n.parent IS NULL')
394
            ->andWhere('n.deleted != 1');
395
396
        return $qb->getQuery()->getResult();
397
    }
398
399
    /**
400
     * Create a node translation for a given node
401
     *
402
     * @param HasNodeInterface $hasNode The hasNode
403
     * @param string           $lang    The locale
404
     * @param Node             $node    The node
405
     * @param BaseUser         $owner   The user
406
     *
407
     * @throws \InvalidArgumentException
408
     *
409
     * @return NodeTranslation
410
     */
411
    public function createNodeTranslationFor(
412
        HasNodeInterface $hasNode,
413
        $lang,
414
        Node $node,
415
        BaseUser $owner
416
    ) {
417
        $em = $this->getEntityManager();
418
        $className = ClassLookup::getClass($hasNode);
419
        if (!$hasNode->getId() > 0) {
420
            throw new \InvalidArgumentException('The entity of class ' . $className . ' has no id, maybe you forgot to flush first');
421
        }
422
423
        $nodeTranslation = new NodeTranslation();
424
        $nodeTranslation
425
            ->setNode($node)
426
            ->setLang($lang)
427
            ->setTitle($hasNode->getTitle())
428
            ->setOnline(false)
429
            ->setWeight(0);
430
431
        $em->persist($nodeTranslation);
432
433
        $nodeVersion = $em->getRepository(NodeVersion::class)
434
            ->createNodeVersionFor(
435
                $hasNode,
436
                $nodeTranslation,
437
                $owner,
438
                null
439
            );
440
441
        $nodeTranslation->setPublicNodeVersion($nodeVersion);
442
        $em->persist($nodeTranslation);
443
        $em->flush();
444
        $em->refresh($nodeTranslation);
445
        $em->refresh($node);
446
447
        return $nodeTranslation;
448
    }
449
450
    /**
451
     * Add a draft node version for a given node
452
     *
453
     * @param HasNodeInterface $hasNode The hasNode
454
     * @param string           $lang    The locale
455
     * @param Node             $node    The node
456
     * @param BaseUser         $owner   The user
457
     *
458
     * @throws \InvalidArgumentException
459
     *
460
     * @return NodeTranslation
461
     */
462
    public function addDraftNodeVersionFor(
463
        HasNodeInterface $hasNode,
464
        $lang,
465
        Node $node,
466
        BaseUser $owner
467
    ) {
468
        $em = $this->getEntityManager();
469
        $className = ClassLookup::getClass($hasNode);
470
        if (!$hasNode->getId() > 0) {
471
            throw new \InvalidArgumentException('The entity of class ' . $className . ' has no id, maybe you forgot to flush first');
472
        }
473
474
        $nodeTranslation = $em->getRepository(NodeTranslation::class)->findOneBy(['lang' => $lang, 'node' => $node]);
475
476
        $em->getRepository(NodeVersion::class)
477
            ->createNodeVersionFor(
478
                $hasNode,
479
                $nodeTranslation,
480
                $owner,
481
                null,
482
                NodeVersion::DRAFT_VERSION
483
            );
484
485
        $em->refresh($nodeTranslation);
486
        $em->refresh($node);
487
488
        return $nodeTranslation;
489
    }
490
491
    /**
492
     * Find best match for given URL and locale
493
     *
494
     * @param string $urlSlug The slug
495
     * @param string $locale  The locale
496
     *
497
     * @return NodeTranslation
498
     */
499
    public function getBestMatchForUrl($urlSlug, $locale)
500
    {
501
        $em = $this->getEntityManager();
502
503
        $rsm = new ResultSetMappingBuilder($em);
504
        $rsm->addRootEntityFromClassMetadata(
505
            'Kunstmaan\NodeBundle\Entity\NodeTranslation',
506
            'nt'
507
        );
508
509
        $query = $em
510
            ->createNativeQuery(
511
                'select nt.*
512
                from kuma_node_translations nt
513
                join kuma_nodes n on n.id = nt.node_id
514
                where n.deleted = 0 and nt.lang = :lang and locate(nt.url, :url) = 1
515
                order by length(nt.url) desc limit 1',
516
                $rsm
517
            );
518
        $query->setParameter('lang', $locale);
519
        $query->setParameter('url', $urlSlug);
520
521
        return $query->getOneOrNullResult();
522
    }
523
524
    /**
525
     * Test if all parents of the specified NodeTranslation have a node
526
     * translation for the specified language
527
     *
528
     * @param NodeTranslation $nodeTranslation The node translation
529
     * @param string          $language        The locale
530
     *
531
     * @return bool
532
     */
533
    public function hasParentNodeTranslationsForLanguage(
534
        NodeTranslation $nodeTranslation,
535
        $language
536
    ) {
537
        $parentNode = $nodeTranslation->getNode()->getParent();
538
        if ($parentNode !== null) {
539
            $parentNodeTranslation = $parentNode->getNodeTranslation(
540
                $language,
541
                true
542
            );
543
            if ($parentNodeTranslation !== null) {
544
                return $this->hasParentNodeTranslationsForLanguage(
545
                    $parentNodeTranslation,
546
                    $language
547
                );
548
            }
549
550
            return false;
551
        }
552
553
        return true;
554
    }
555
556
    /**
557
     * This will return 1 NodeTranslation by default (if one exists).
558
     * Just give it the internal name as defined on the Node in the database
559
     * and the language.
560
     *
561
     * It'll only return the latest version. It'll also hide deleted & offline
562
     * nodes.
563
     *
564
     * @param $language
565
     * @param $internalName
566
     */
567
    public function getNodeTranslationByLanguageAndInternalName(
568
        $language,
569
        $internalName
570
    ) {
571
        $qb = $this->createQueryBuilder('nt')
572
            ->select('nt', 'v')
573
            ->innerJoin('nt.node', 'n', 'WITH', 'nt.node = n.id')
574
            ->leftJoin(
575
                'nt.publicNodeVersion',
576
                'v',
577
                'WITH',
578
                'nt.publicNodeVersion = v.id'
579
            )
580
            ->where('n.deleted != 1')
581
            ->andWhere('nt.online = 1')
582
            ->setFirstResult(0)
583
            ->setMaxResults(1);
584
585
        $qb->andWhere('nt.lang = :lang')
586
            ->setParameter('lang', $language);
587
588
        $qb->andWhere('n.internalName = :internal_name')
589
            ->setParameter('internal_name', $internalName);
590
591
        return $qb->getQuery()->getOneOrNullResult();
592
    }
593
594
    public function getAllNodeTranslationsByRefEntityName($refEntityName)
595
    {
596
        $qb = $this->createQueryBuilder('nt')
597
            ->select('nt,n')
598
            ->innerJoin('nt.publicNodeVersion', 'nv')
599
            ->innerJoin('nt.node', 'n')
600
            ->where('nv.refEntityName = :refEntityName')
601
            ->setParameter('refEntityName', $refEntityName);
602
603
        return $qb->getQuery()->getResult();
604
    }
605
606
    public function getParentNodeTranslation(NodeTranslation $nodeTranslation)
607
    {
608
        $parent = $nodeTranslation->getNode()->getParent();
609
        if (\is_null($parent)) {
610
            return null;
611
        }
612
613
        $qb = $this->createQueryBuilder('nt')
614
            ->select('nt,n')
615
            ->innerJoin('nt.publicNodeVersion', 'nv')
616
            ->innerJoin('nt.node', 'n')
617
            ->where('nt.node = :parent')
618
            ->andWhere('n.deleted = 0')
619
            ->andWhere('nt.lang = :lang')
620
            ->setParameter('parent', $parent)
621
            ->setParameter('lang', $nodeTranslation->getLang());
622
623
        return $qb->getQuery()->getOneOrNullResult();
624
    }
625
}
626