NodePagesConfiguration::setAclProvider()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 3
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
namespace Kunstmaan\NodeSearchBundle\Configuration;
4
5
use Doctrine\ORM\EntityManager;
6
use Elastica\Index;
7
use Elastica\Type\Mapping;
8
use Kunstmaan\AdminBundle\Helper\DomainConfigurationInterface;
9
use Kunstmaan\AdminBundle\Helper\Security\Acl\Permission\MaskBuilder;
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\PageInterface;
15
use Kunstmaan\NodeBundle\Helper\RenderContext;
16
use Kunstmaan\NodeSearchBundle\Event\IndexNodeEvent;
17
use Kunstmaan\NodeSearchBundle\Helper\IndexablePagePartsService;
18
use Kunstmaan\NodeSearchBundle\Helper\SearchViewTemplateInterface;
19
use Kunstmaan\NodeSearchBundle\Services\SearchViewRenderer;
20
use Kunstmaan\PagePartBundle\Helper\HasPagePartsInterface;
21
use Kunstmaan\SearchBundle\Configuration\SearchConfigurationInterface;
22
use Kunstmaan\SearchBundle\Provider\SearchProviderInterface;
23
use Kunstmaan\SearchBundle\Search\AnalysisFactoryInterface;
24
use Kunstmaan\UtilitiesBundle\Helper\ClassLookup;
25
use Psr\Log\LoggerInterface;
26
use Symfony\Component\DependencyInjection\ContainerInterface;
27
use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
28
use Symfony\Component\HttpFoundation\Request;
29
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
30
use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
31
use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
32
use Symfony\Component\Security\Acl\Model\AclInterface;
33
use Symfony\Component\Security\Acl\Model\AclProviderInterface;
34
use Symfony\Component\Security\Acl\Model\AuditableEntryInterface;
35
use Symfony\Component\Templating\EngineInterface;
36
37
class NodePagesConfiguration implements SearchConfigurationInterface
38
{
39
    /** @var string */
40
    protected $indexName;
41
42
    /** @var string */
43
    protected $indexType;
44
45
    /** @var SearchProviderInterface */
46
    protected $searchProvider;
47
48
    /** @var array */
49
    protected $locales = [];
50
51
    /** @var array */
52
    protected $analyzerLanguages;
53
54
    /** @var EntityManager */
55
    protected $em;
56
57
    /** @var array */
58
    protected $documents = [];
59
60
    /** @var ContainerInterface */
61
    protected $container;
62
63
    /** @var AclProviderInterface */
64
    protected $aclProvider = null;
65
66
    /** @var LoggerInterface */
67
    protected $logger = null;
68
69
    /** @var IndexablePagePartsService */
70
    protected $indexablePagePartsService;
71
72
    /** @var DomainConfigurationInterface */
73
    protected $domainConfiguration;
74
75
    /** @var array */
76
    protected $properties = [];
77
78
    /** @var int */
79
    protected $numberOfShards;
80
81
    /** @var int */
82
    protected $numberOfReplicas;
83
84
    /** @var Node */
85
    protected $currentTopNode = null;
86
87
    /** @var array */
88
    protected $nodeRefs = [];
89
90
    /**
91
     * @param ContainerInterface      $container
92
     * @param SearchProviderInterface $searchProvider
93
     * @param string                  $name
94
     * @param string                  $type
95
     */
96
    public function __construct($container, $searchProvider, $name, $type, $numberOfShards = 1, $numberOfReplicas = 0)
97
    {
98
        $this->container = $container;
99
        $this->indexName = $name;
100
        $this->indexType = $type;
101
        $this->searchProvider = $searchProvider;
102
        $this->domainConfiguration = $this->container->get('kunstmaan_admin.domain_configuration');
103
        $this->locales = $this->domainConfiguration->getBackendLocales();
104
        $this->analyzerLanguages = $this->container->getParameter('analyzer_languages');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->container->getPar...r('analyzer_languages') of type * is incompatible with the declared type array of property $analyzerLanguages.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
105
        $this->em = $this->container->get('doctrine')->getManager();
106
        $this->numberOfShards = $numberOfShards;
107
        $this->numberOfReplicas = $numberOfReplicas;
108
    }
109
110
    /**
111
     * @param AclProviderInterface $aclProvider
112
     */
113
    public function setAclProvider(AclProviderInterface $aclProvider)
114
    {
115
        $this->aclProvider = $aclProvider;
116
    }
117
118
    /**
119
     * @param IndexablePagePartsService $indexablePagePartsService
120
     */
121
    public function setIndexablePagePartsService(IndexablePagePartsService $indexablePagePartsService)
122
    {
123
        $this->indexablePagePartsService = $indexablePagePartsService;
124
    }
125
126
    /**
127
     * @param array $properties
128
     */
129
    public function setDefaultProperties(array $properties)
130
    {
131
        $this->properties = array_merge($this->properties, $properties);
132
    }
133
134
    /**
135
     * @param LoggerInterface $logger
136
     */
137
    public function setLogger(LoggerInterface $logger)
138
    {
139
        $this->logger = $logger;
140
    }
141
142
    /**
143
     * @return array
144
     */
145
    public function getLanguagesNotAnalyzed()
146
    {
147
        $notAnalyzed = [];
148
        foreach ($this->locales as $locale) {
149
            if (preg_match('/[a-z]{2}_?+[a-zA-Z]{2}/', $locale)) {
150
                $locale = strtolower($locale);
151
            }
152
153
            if (false === \array_key_exists($locale, $this->analyzerLanguages)) {
154
                $notAnalyzed[] = $locale;
155
            }
156
        }
157
158
        return $notAnalyzed;
159
    }
160
161
    /**
162
     * Create node index
163
     */
164
    public function createIndex()
165
    {
166
        //create analysis
167
        $analysis = $this->container->get(
168
            'kunstmaan_search.search.factory.analysis'
169
        );
170
171
        foreach ($this->locales as $locale) {
172
            // Multilanguage check
173
            if (preg_match('/[a-z]{2}_?+[a-zA-Z]{2}/', $locale)) {
174
                $locale = strtolower($locale);
175
            }
176
177
            // Build new index
178
            $index = $this->searchProvider->createIndex($this->indexName . '_' . $locale);
179
180
            if (\array_key_exists($locale, $this->analyzerLanguages)) {
181
                $localeAnalysis = clone $analysis;
182
                $language = $this->analyzerLanguages[$locale]['analyzer'];
183
184
                // Create index with analysis
185
                $this->setAnalysis($index, $localeAnalysis->setupLanguage($language));
186
            } else {
187
                $index->create();
188
            }
189
190
            $this->setMapping($index, $locale);
191
        }
192
    }
193
194
    /**
195
     * Populate node index
196
     */
197
    public function populateIndex()
198
    {
199
        $nodeRepository = $this->em->getRepository(Node::class);
200
        $nodes = $nodeRepository->getAllTopNodes();
201
202
        foreach ($nodes as $node) {
203
            $this->currentTopNode = $node;
204
            foreach ($this->locales as $lang) {
205
                $this->createNodeDocuments($node, $lang);
206
            }
207
        }
208
209
        if (!empty($this->documents)) {
210
            $this->searchProvider->addDocuments($this->documents);
211
            $this->documents = [];
212
        }
213
    }
214
215
    /**
216
     * Index a node (including its children) - for the specified language only
217
     *
218
     * @param Node   $node
219
     * @param string $lang
220
     */
221
    public function indexNode(Node $node, $lang)
222
    {
223
        $this->createNodeDocuments($node, $lang);
224
225
        if (!empty($this->documents)) {
226
            $this->searchProvider->addDocuments($this->documents);
227
            $this->documents = [];
228
        }
229
    }
230
231
    /**
232
     * Add documents for the node translation (and children) to the index
233
     *
234
     * @param Node   $node
235
     * @param string $lang
236
     */
237
    public function createNodeDocuments(Node $node, $lang)
238
    {
239
        $nodeTranslation = $node->getNodeTranslation($lang, true);
240
        if ($nodeTranslation && $this->indexNodeTranslation($nodeTranslation)) {
241
            $this->indexChildren($node, $lang);
242
        }
243
    }
244
245
    /**
246
     * Index all children of the specified node (only for the specified
247
     * language)
248
     *
249
     * @param Node   $node
250
     * @param string $lang
251
     */
252
    public function indexChildren(Node $node, $lang)
253
    {
254
        foreach ($node->getChildren() as $childNode) {
255
            $this->indexNode($childNode, $lang);
256
        }
257
    }
258
259
    /**
260
     * Index a node translation
261
     *
262
     * @param NodeTranslation $nodeTranslation
263
     * @param bool            $add             Add node immediately to index?
264
     *
265
     * @return bool Return true if the document has been indexed
266
     */
267
    public function indexNodeTranslation(NodeTranslation $nodeTranslation, $add = false)
268
    {
269
        // Retrieve the public NodeVersion
270
        $publicNodeVersion = $nodeTranslation->getPublicNodeVersion();
271
        if (\is_null($publicNodeVersion)) {
272
            return false;
273
        }
274
275
        $refPage = $this->getNodeRefPage($publicNodeVersion);
276
        if ($refPage->isStructureNode()) {
277
            return true;
278
        }
279
280
        // Only index online NodeTranslations
281
        if (!$nodeTranslation->isOnline()) {
282
            return false;
283
        }
284
285
        $node = $nodeTranslation->getNode();
286
        if ($this->isIndexable($refPage)) {
287
            // Retrieve the referenced entity from the public NodeVersion
288
            $page = $publicNodeVersion->getRef($this->em);
289
290
            $this->addPageToIndex($nodeTranslation, $node, $publicNodeVersion, $page);
0 ignored issues
show
Bug introduced by
It seems like $page defined by $publicNodeVersion->getRef($this->em) on line 288 can be null; however, Kunstmaan\NodeSearchBund...ation::addPageToIndex() 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...
291
            if ($add) {
292
                $this->searchProvider->addDocuments($this->documents);
293
                $this->documents = [];
294
            }
295
        }
296
297
        return true; // return true even if the page itself should not be indexed. This makes sure its children are being processed (i.e. structured nodes)
298
    }
299
300
    /**
301
     * Return if the page is indexable - by default all pages are indexable,
302
     * you can override this by implementing the IndexableInterface on your
303
     * page entity and returning false in the isIndexable method.
304
     *
305
     * @param HasNodeInterface $page
306
     *
307
     * @return bool
308
     */
309
    protected function isIndexable(HasNodeInterface $page)
310
    {
311
        return $this->container->get('kunstmaan_node.pages_configuration')->isIndexable($page);
312
    }
313
314
    /**
315
     * Remove the specified node translation from the index
316
     *
317
     * @param NodeTranslation $nodeTranslation
318
     */
319
    public function deleteNodeTranslation(NodeTranslation $nodeTranslation)
320
    {
321
        $uid = 'nodetranslation_' . $nodeTranslation->getId();
322
        $indexName = $this->indexName . '_' . $nodeTranslation->getLang();
323
        $this->searchProvider->deleteDocument($indexName, $this->indexType, $uid);
324
    }
325
326
    /**
327
     * Delete the specified index
328
     */
329
    public function deleteIndex()
330
    {
331
        foreach ($this->locales as $locale) {
332
            $this->searchProvider->deleteIndex($this->indexName . '_' . $locale);
333
        }
334
    }
335
336
    /**
337
     * Apply the analysis factory to the index
338
     *
339
     * @param Index                    $index
340
     * @param AnalysisFactoryInterface $analysis
341
     */
342
    public function setAnalysis(Index $index, AnalysisFactoryInterface $analysis)
343
    {
344
        $analysers = $analysis->build();
345
        $args = [
346
            'number_of_shards' => $this->numberOfShards,
347
            'number_of_replicas' => $this->numberOfReplicas,
348
            'analysis' => $analysers,
349
        ];
350
351
        if (class_exists(\Elastica\Mapping::class)) {
352
            $args = [
353
                'settings' => [
354
                    'number_of_shards' => $this->numberOfShards,
355
                    'number_of_replicas' => $this->numberOfReplicas,
356
                    'analysis' => $analysers,
357
                ],
358
            ];
359
360
            $ngramDiff = 1;
361
            if (isset($analysers['tokenizer']) && count($analysers['tokenizer']) > 0) {
362
                foreach ($analysers['tokenizer'] as $tokenizer) {
363
                    if ($tokenizer['type'] === 'nGram') {
364
                        $diff = $tokenizer['max_gram'] - $tokenizer['min_gram'];
365
366
                        $ngramDiff = $diff > $ngramDiff ? $diff : $ngramDiff;
367
                    }
368
                }
369
            }
370
371
            if ($ngramDiff > 1) {
372
                $args['settings']['max_ngram_diff'] = $ngramDiff;
373
            }
374
        }
375
376
        $index->create($args);
377
    }
378
379
    /**
380
     * Return default search fields mapping for node translations
381
     *
382
     * @param Index  $index
383
     * @param string $lang
384
     *
385
     * @return Mapping|\Elastica\Mapping
386
     */
387
    protected function createDefaultSearchFieldsMapping(Index $index, $lang = 'en')
388
    {
389
        if (class_exists(\Elastica\Type\Mapping::class)) {
390
            $mapping = new Mapping();
391
            $mapping->setType($index->getType($this->indexType));
0 ignored issues
show
Bug introduced by
The method getType() does not seem to exist on object<Elastica\Index>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
392
        } else {
393
            $mapping = new \Elastica\Mapping();
394
        }
395
396
        $mapping->setProperties($this->properties);
397
398
        return $mapping;
399
    }
400
401
    /**
402
     * Initialize the index with the default search fields mapping
403
     *
404
     * @param Index  $index
405
     * @param string $lang
406
     */
407
    protected function setMapping(Index $index, $lang = 'en')
408
    {
409
        $mapping = $this->createDefaultSearchFieldsMapping($index, $lang);
410
        if (class_exists(\Elastica\Mapping::class)) {
411
            $mapping->send($index);
412
        } else {
413
            $mapping->send();
0 ignored issues
show
Bug introduced by
The call to send() misses a required argument $index.

This check looks for function calls that miss required arguments.

Loading history...
414
        }
415
        $index->refresh();
416
    }
417
418
    /**
419
     * Create a search document for a page
420
     *
421
     * @param NodeTranslation  $nodeTranslation
422
     * @param Node             $node
423
     * @param NodeVersion      $publicNodeVersion
424
     * @param HasNodeInterface $page
425
     */
426
    protected function addPageToIndex(
427
        NodeTranslation $nodeTranslation,
428
        Node $node,
429
        NodeVersion $publicNodeVersion,
430
        HasNodeInterface $page
431
    ) {
432
        $rootNode = $this->currentTopNode;
433
        if (!$rootNode) {
434
            // Fetch main parent of current node...
435
            $rootNode = $this->em->getRepository(Node::class)->getRootNodeFor(
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method getRootNodeFor() does only exist in the following implementations of said interface: Kunstmaan\NodeBundle\Repository\NodeRepository.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
436
                $node,
437
                $nodeTranslation->getLang()
438
            );
439
        }
440
441
        $doc = array(
442
            'root_id' => $rootNode->getId(),
443
            'node_id' => $node->getId(),
444
            'node_translation_id' => $nodeTranslation->getId(),
445
            'node_version_id' => $publicNodeVersion->getId(),
446
            'title' => $nodeTranslation->getTitle(),
447
            'slug' => $nodeTranslation->getFullSlug(),
448
            'page_class' => ClassLookup::getClass($page),
449
            'created' => $this->getUTCDateTime(
450
                $nodeTranslation->getCreated()
451
            )->format(\DateTime::ISO8601),
452
            'updated' => $this->getUTCDateTime(
453
                $nodeTranslation->getUpdated()
454
            )->format(\DateTime::ISO8601),
455
        );
456
        if ($this->logger) {
457
            $this->logger->info('Indexing document : ' . implode(', ', $doc));
458
        }
459
460
        // Permissions
461
        $this->addPermissions($node, $doc);
462
463
        // Search type
464
        $this->addSearchType($page, $doc);
465
466
        // Parent and Ancestors
467
        $this->addParentAndAncestors($node, $doc);
468
469
        // Content
470
        $this->addPageContent($nodeTranslation, $page, $doc);
471
472
        // Add document to index
473
        $uid = 'nodetranslation_' . $nodeTranslation->getId();
474
475
        $this->addCustomData($page, $doc);
476
477
        $this->documents[] = $this->searchProvider->createDocument(
478
            $uid,
479
            $doc,
0 ignored issues
show
Documentation introduced by
$doc is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
480
            $this->indexName . '_' . $nodeTranslation->getLang(),
481
            $this->indexType
482
        );
483
    }
484
485
    /**
486
     * Add view permissions to the index document
487
     *
488
     * @param Node  $node
489
     * @param array $doc
490
     *
491
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
492
     */
493
    protected function addPermissions(Node $node, &$doc)
494
    {
495
        if (!\is_null($this->aclProvider)) {
496
            $roles = $this->getAclPermissions($node);
497
        } else {
498
            // Fallback when no ACL available / assume everything is accessible...
499
            $roles = array('IS_AUTHENTICATED_ANONYMOUSLY');
500
        }
501
        $doc['view_roles'] = $roles;
502
    }
503
504
    /**
505
     * Add type to the index document
506
     *
507
     * @param object $page
508
     * @param array  $doc
509
     *
510
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
511
     */
512
    protected function addSearchType($page, &$doc)
513
    {
514
        $doc['type'] = $this->container->get('kunstmaan_node.pages_configuration')->getSearchType($page);
515
    }
516
517
    /**
518
     * Add parent nodes to the index document
519
     *
520
     * @param Node  $node
521
     * @param array $doc
522
     *
523
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
524
     */
525
    protected function addParentAndAncestors($node, &$doc)
526
    {
527
        $parent = $node->getParent();
528
529
        if ($parent) {
530
            $doc['parent'] = $parent->getId();
531
            $ancestors = [];
532
            do {
533
                $ancestors[] = $parent->getId();
534
                $parent = $parent->getParent();
535
            } while ($parent);
536
            $doc['ancestors'] = $ancestors;
537
        }
538
    }
539
540
    /**
541
     * Add page content to the index document
542
     *
543
     * @param NodeTranslation  $nodeTranslation
544
     * @param HasNodeInterface $page
545
     * @param array            $doc
546
     */
547
    protected function addPageContent(NodeTranslation $nodeTranslation, $page, &$doc)
548
    {
549
        $this->enterRequestScope($nodeTranslation->getLang());
550
        if ($this->logger) {
551
            $this->logger->debug(
552
                sprintf(
553
                    'Indexing page "%s" / lang : %s / type : %s / id : %d / node id : %d',
554
                    $page->getTitle(),
555
                    $nodeTranslation->getLang(),
556
                    \get_class($page),
557
                    $page->getId(),
558
                    $nodeTranslation->getNode()->getId()
559
                )
560
            );
561
        }
562
563
        $doc['content'] = '';
564 View Code Duplication
        if ($page instanceof SearchViewTemplateInterface) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
565
            if ($this->isMethodOverridden('renderCustomSearchView')) {
566
                @trigger_error(sprintf('Overriding the "%s" method is deprecated since KunstmaanNodeSearchBundle 5.7 and will be removed in KunstmaanNodeSearchBundle 6.0. Override the "renderCustomSearchView" method of the "%s" service instead.', __METHOD__, SearchViewRenderer::class), E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
567
568
                $doc['content'] = $this->renderCustomSearchView($nodeTranslation, $page, $this->container->get('templating'));
0 ignored issues
show
Documentation introduced by
$this->container->get('templating') is of type object|null, but the function expects a object<Symfony\Component...lating\EngineInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Deprecated Code introduced by
The method Kunstmaan\NodeSearchBund...enderCustomSearchView() has been deprecated with message: This method is deprecated since KunstmaanNodeSearchBundle 5.7 and will be removed in KunstmaanNodeSearchBundle 6.0. Use the "renderCustomSearchView" method of the "Kunstmaan\NodeSearchBundle\Services\SearchViewRenderer" instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
569
            } else {
570
                $searchViewRenderer = $this->container->get('kunstmaan_node_search.service.search_view_renderer');
571
572
                $doc['content'] = $searchViewRenderer->renderCustomSearchView($nodeTranslation, $page, $this->container);
573
            }
574
575
            return null;
576
        }
577
578 View Code Duplication
        if ($page instanceof HasPagePartsInterface) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
579
            if ($this->isMethodOverridden('renderDefaultSearchView')) {
580
                @trigger_error(sprintf('Overriding the "%s" method is deprecated since KunstmaanNodeSearchBundle 5.7 and will be removed in KunstmaanNodeSearchBundle 6.0. Override the "renderDefaultSearchView" method of the "%s" service instead.', __METHOD__, SearchViewRenderer::class), E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
581
582
                $doc['content'] = $this->renderDefaultSearchView($nodeTranslation, $page, $this->container->get('templating'));
0 ignored issues
show
Documentation introduced by
$this->container->get('templating') is of type object|null, but the function expects a object<Symfony\Component...lating\EngineInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Deprecated Code introduced by
The method Kunstmaan\NodeSearchBund...nderDefaultSearchView() has been deprecated with message: This method is deprecated since KunstmaanNodeSearchBundle 5.7 and will be removed in KunstmaanNodeSearchBundle 6.0. Use the "renderDefaultSearchView" method of the "Kunstmaan\NodeSearchBundle\Services\SearchViewRenderer" instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
583
            } else {
584
                $searchViewRenderer = $this->container->get('kunstmaan_node_search.service.search_view_renderer');
585
586
                $doc['content'] = $searchViewRenderer->renderDefaultSearchView($nodeTranslation, $page);
587
            }
588
589
            return null;
590
        }
591
    }
592
593
    /**
594
     * Enter request scope if it is not active yet...
595
     *
596
     * @param string $lang
597
     */
598
    protected function enterRequestScope($lang)
599
    {
600
        $locale = null;
601
        $requestStack = $this->container->get('request_stack');
602
        // If there already is a request, get the locale from it.
603
        if ($requestStack->getCurrentRequest()) {
604
            $locale = $requestStack->getCurrentRequest()->getLocale();
605
        }
606
        // If we don't have a request or the current request locale is different from the node langauge
607
        if (!$requestStack->getCurrentRequest() || ($locale && $locale !== $lang)) {
608
            $request = new Request();
609
            $request->setLocale($lang);
610
611
            $context = $this->container->get('router')->getContext();
612
            $context->setParameter('_locale', $lang);
613
614
            $requestStack->push($request);
615
        }
616
    }
617
618
    /**
619
     * Render a custom search view
620
     *
621
     * @deprecated This method is deprecated since KunstmaanNodeSearchBundle 5.7 and will be removed in KunstmaanNodeSearchBundle 6.0. Use the "renderCustomSearchView" method of the "Kunstmaan\NodeSearchBundle\Services\SearchViewRenderer" instead.
622
     *
623
     * @param NodeTranslation             $nodeTranslation
624
     * @param SearchViewTemplateInterface $page
625
     * @param EngineInterface             $renderer
626
     *
627
     * @return string
628
     */
629
    protected function renderCustomSearchView(
630
        NodeTranslation $nodeTranslation,
631
        SearchViewTemplateInterface $page,
632
        EngineInterface $renderer
633
    ) {
634
        @trigger_error(sprintf('The "%s" method is deprecated since KunstmaanNodeSearchBundle 5.7 and will be removed in KunstmaanNodeSearchBundle 6.0. Use the "%s" service with method "renderCustomSearchView" instead.', __METHOD__, SearchViewRenderer::class), E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
635
636
        $view = $page->getSearchView();
637
        $renderContext = new RenderContext([
638
            'locale' => $nodeTranslation->getLang(),
639
            'page' => $page,
640
            'indexMode' => true,
641
            'nodetranslation' => $nodeTranslation,
642
        ]);
643
644
        if ($page instanceof PageInterface) {
645
            $request = $this->container->get('request_stack')->getCurrentRequest();
646
            $page->service($this->container, $request, $renderContext);
647
        }
648
649
        $content = $this->removeHtml(
0 ignored issues
show
Deprecated Code introduced by
The method Kunstmaan\NodeSearchBund...iguration::removeHtml() has been deprecated with message: This method is deprecated since KunstmaanNodeSearchBundle 5.7 and will be removed in KunstmaanNodeSearchBundle 6.0. Use the "removeHtml" method of the "Kunstmaan\NodeSearchBundle\Services\SearchViewRenderer" instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
650
            $renderer->render(
651
                $view,
652
                $renderContext->getArrayCopy()
653
            )
654
        );
655
656
        return $content;
657
    }
658
659
    /**
660
     * Render default search view (all indexable pageparts in the main context
661
     * of the page)
662
     *
663
     * @deprecated This method is deprecated since KunstmaanNodeSearchBundle 5.7 and will be removed in KunstmaanNodeSearchBundle 6.0. Use the "renderDefaultSearchView" method of the "Kunstmaan\NodeSearchBundle\Services\SearchViewRenderer" instead.
664
     *
665
     * @param NodeTranslation       $nodeTranslation
666
     * @param HasPagePartsInterface $page
667
     * @param EngineInterface       $renderer
668
     *
669
     * @return string
670
     */
671
    protected function renderDefaultSearchView(
672
        NodeTranslation $nodeTranslation,
673
        HasPagePartsInterface $page,
674
        EngineInterface $renderer
675
    ) {
676
        @trigger_error(sprintf('The "%s" method is deprecated since KunstmaanNodeSearchBundle 5.7 and will be removed in KunstmaanNodeSearchBundle 6.0. Use the "%s" service with method "renderDefaultSearchView" instead.', __METHOD__, SearchViewRenderer::class), E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
677
678
        $pageparts = $this->indexablePagePartsService->getIndexablePageParts($page);
679
        $view = '@KunstmaanNodeSearch/PagePart/view.html.twig';
680
        $content = $this->removeHtml(
0 ignored issues
show
Deprecated Code introduced by
The method Kunstmaan\NodeSearchBund...iguration::removeHtml() has been deprecated with message: This method is deprecated since KunstmaanNodeSearchBundle 5.7 and will be removed in KunstmaanNodeSearchBundle 6.0. Use the "removeHtml" method of the "Kunstmaan\NodeSearchBundle\Services\SearchViewRenderer" instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
681
            $renderer->render(
682
                $view,
683
                array(
684
                    'locale' => $nodeTranslation->getLang(),
685
                    'page' => $page,
686
                    'pageparts' => $pageparts,
687
                    'indexMode' => true,
688
                )
689
            )
690
        );
691
692
        return $content;
693
    }
694
695
    /**
696
     * Add custom data to index document (you can override to add custom fields
697
     * to the search index)
698
     *
699
     * @param HasNodeInterface $page
700
     * @param array            $doc
701
     */
702
    protected function addCustomData(HasNodeInterface $page, &$doc)
703
    {
704
        $event = new IndexNodeEvent($page, $doc);
705
        $this->dispatch($event, IndexNodeEvent::EVENT_INDEX_NODE);
706
707
        $doc = $event->doc;
708
709
        if ($page instanceof HasCustomSearchDataInterface) {
710
            $doc += $page->getCustomSearchData($doc);
711
        }
712
    }
713
714
    /**
715
     * Convert a DateTime to UTC equivalent...
716
     *
717
     * @param \DateTime $dateTime
718
     *
719
     * @return \DateTime
720
     */
721
    protected function getUTCDateTime(\DateTime $dateTime)
722
    {
723
        $result = clone $dateTime;
724
        $result->setTimezone(new \DateTimeZone('UTC'));
725
726
        return $result;
727
    }
728
729
    /**
730
     * Removes all HTML markup & decode HTML entities
731
     *
732
     * @deprecated This method is deprecated since KunstmaanNodeSearchBundle 5.7 and will be removed in KunstmaanNodeSearchBundle 6.0. Use the "removeHtml" method of the "Kunstmaan\NodeSearchBundle\Services\SearchViewRenderer" instead.
733
     *
734
     * @param $text
735
     *
736
     * @return string
737
     */
738
    protected function removeHtml($text)
739
    {
740
        @trigger_error(sprintf('The "%s" method is deprecated since KunstmaanNodeSearchBundle 5.7 and will be removed in KunstmaanNodeSearchBundle 6.0. Use the "removeHtml" method of the "%s" service instead.', __METHOD__, SearchViewRenderer::class), E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
741
742
        $searchViewRenderer = $this->container->get('kunstmaan_node_search.service.search_view_renderer');
743
744
        return $searchViewRenderer->removeHtml($text);
745
    }
746
747
    /**
748
     * Fetch ACL permissions for the specified entity
749
     *
750
     * @param object $object
751
     *
752
     * @return array
753
     */
754
    protected function getAclPermissions($object)
755
    {
756
        $roles = [];
757
758
        try {
759
            $objectIdentity = ObjectIdentity::fromDomainObject($object);
760
761
            /* @var AclInterface $acl */
762
            $acl = $this->aclProvider->findAcl($objectIdentity);
763
            $objectAces = $acl->getObjectAces();
764
765
            /* @var AuditableEntryInterface $ace */
766 View Code Duplication
            foreach ($objectAces as $ace) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
767
                $securityIdentity = $ace->getSecurityIdentity();
768
                if (
769
                    $securityIdentity instanceof RoleSecurityIdentity &&
770
                    ($ace->getMask() & MaskBuilder::MASK_VIEW != 0)
771
                ) {
772
                    $roles[] = $securityIdentity->getRole();
773
                }
774
            }
775
        } catch (AclNotFoundException $e) {
776
            // No ACL found... assume default
777
            $roles = array('IS_AUTHENTICATED_ANONYMOUSLY');
778
        }
779
780
        return $roles;
781
    }
782
783
    /**
784
     * @param $publicNodeVersion
785
     *
786
     * @return mixed
787
     */
788
    private function getNodeRefPage(NodeVersion $publicNodeVersion)
789
    {
790
        $refEntityName = $publicNodeVersion->getRefEntityName();
791
792
        if (!isset($this->nodeRefs[$refEntityName])) {
793
            $this->nodeRefs[$refEntityName] = new $refEntityName();
794
        }
795
796
        return $this->nodeRefs[$refEntityName];
797
    }
798
799
    /**
800
     * @param object $event
801
     * @param string $eventName
802
     *
803
     * @return object
804
     */
805 View Code Duplication
    private function dispatch($event, string $eventName)
0 ignored issues
show
Duplication introduced by
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...
806
    {
807
        $eventDispatcher = $this->container->get('event_dispatcher');
808
        if (class_exists(LegacyEventDispatcherProxy::class)) {
809
            $eventDispatcher = LegacyEventDispatcherProxy::decorate($eventDispatcher);
810
811
            return $eventDispatcher->dispatch($event, $eventName);
812
        }
813
814
        return $eventDispatcher->dispatch($eventName, $event);
815
    }
816
817
    private function isMethodOverridden(string $method)
818
    {
819
        $reflector = new \ReflectionMethod($this, $method);
820
821
        return $reflector->getDeclaringClass()->getName() !== __CLASS__;
822
    }
823
}
824