Completed
Pull Request — master (#2744)
by Jeroen
06:30
created

NodePagesConfiguration::isMethodOverridden()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
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\HttpFoundation\Request;
28
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
29
use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
30
use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
31
use Symfony\Component\Security\Acl\Model\AclInterface;
32
use Symfony\Component\Security\Acl\Model\AclProviderInterface;
33
use Symfony\Component\Security\Acl\Model\AuditableEntryInterface;
34
use Symfony\Component\Templating\EngineInterface;
35
36
class NodePagesConfiguration implements SearchConfigurationInterface
37
{
38
    /** @var string */
39
    protected $indexName;
40
41
    /** @var string */
42
    protected $indexType;
43
44
    /** @var SearchProviderInterface */
45
    protected $searchProvider;
46
47
    /** @var array */
48
    protected $locales = [];
49
50
    /** @var array */
51
    protected $analyzerLanguages;
52
53
    /** @var EntityManager */
54
    protected $em;
55
56
    /** @var array */
57
    protected $documents = [];
58
59
    /** @var ContainerInterface */
60
    protected $container;
61
62
    /** @var AclProviderInterface */
63
    protected $aclProvider = null;
64
65
    /** @var LoggerInterface */
66
    protected $logger = null;
67
68
    /** @var IndexablePagePartsService */
69
    protected $indexablePagePartsService;
70
71
    /** @var DomainConfigurationInterface */
72
    protected $domainConfiguration;
73
74
    /** @var array */
75
    protected $properties = [];
76
77
    /** @var int */
78
    protected $numberOfShards;
79
80
    /** @var int */
81
    protected $numberOfReplicas;
82
83
    /** @var Node */
84
    protected $currentTopNode = null;
85
86
    /** @var array */
87
    protected $nodeRefs = [];
88
89
    /**
90
     * @param ContainerInterface      $container
91
     * @param SearchProviderInterface $searchProvider
92
     * @param string                  $name
93
     * @param string                  $type
94
     */
95
    public function __construct($container, $searchProvider, $name, $type, $numberOfShards = 1, $numberOfReplicas = 0)
96
    {
97
        $this->container = $container;
98
        $this->indexName = $name;
99
        $this->indexType = $type;
100
        $this->searchProvider = $searchProvider;
101
        $this->domainConfiguration = $this->container->get('kunstmaan_admin.domain_configuration');
102
        $this->locales = $this->domainConfiguration->getBackendLocales();
103
        $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...
104
        $this->em = $this->container->get('doctrine')->getManager();
105
        $this->numberOfShards = $numberOfShards;
106
        $this->numberOfReplicas = $numberOfReplicas;
107
    }
108
109
    /**
110
     * @param AclProviderInterface $aclProvider
111
     */
112
    public function setAclProvider(AclProviderInterface $aclProvider)
113
    {
114
        $this->aclProvider = $aclProvider;
115
    }
116
117
    /**
118
     * @param IndexablePagePartsService $indexablePagePartsService
119
     */
120
    public function setIndexablePagePartsService(IndexablePagePartsService $indexablePagePartsService)
121
    {
122
        $this->indexablePagePartsService = $indexablePagePartsService;
123
    }
124
125
    /**
126
     * @param array $properties
127
     */
128
    public function setDefaultProperties(array $properties)
129
    {
130
        $this->properties = array_merge($this->properties, $properties);
131
    }
132
133
    /**
134
     * @param LoggerInterface $logger
135
     */
136
    public function setLogger(LoggerInterface $logger)
137
    {
138
        $this->logger = $logger;
139
    }
140
141
    /**
142
     * @return array
143
     */
144
    public function getLanguagesNotAnalyzed()
145
    {
146
        $notAnalyzed = [];
147
        foreach ($this->locales as $locale) {
148
            if (preg_match('/[a-z]{2}_?+[a-zA-Z]{2}/', $locale)) {
149
                $locale = strtolower($locale);
150
            }
151
152
            if (false === \array_key_exists($locale, $this->analyzerLanguages)) {
153
                $notAnalyzed[] = $locale;
154
            }
155
        }
156
157
        return $notAnalyzed;
158
    }
159
160
    /**
161
     * Create node index
162
     */
163
    public function createIndex()
164
    {
165
        //create analysis
166
        $analysis = $this->container->get(
167
            'kunstmaan_search.search.factory.analysis'
168
        );
169
170
        foreach ($this->locales as $locale) {
171
            // Multilanguage check
172
            if (preg_match('/[a-z]{2}_?+[a-zA-Z]{2}/', $locale)) {
173
                $locale = strtolower($locale);
174
            }
175
176
            // Build new index
177
            $index = $this->searchProvider->createIndex($this->indexName . '_' . $locale);
178
179
            if (\array_key_exists($locale, $this->analyzerLanguages)) {
180
                $localeAnalysis = clone $analysis;
181
                $language = $this->analyzerLanguages[$locale]['analyzer'];
182
183
                // Create index with analysis
184
                $this->setAnalysis($index, $localeAnalysis->setupLanguage($language));
185
            } else {
186
                $index->create();
187
            }
188
189
            $this->setMapping($index, $locale);
190
        }
191
    }
192
193
    /**
194
     * Populate node index
195
     */
196
    public function populateIndex()
197
    {
198
        $nodeRepository = $this->em->getRepository(Node::class);
199
        $nodes = $nodeRepository->getAllTopNodes();
200
201
        foreach ($nodes as $node) {
202
            $this->currentTopNode = $node;
203
            foreach ($this->locales as $lang) {
204
                $this->createNodeDocuments($node, $lang);
205
            }
206
        }
207
208
        if (!empty($this->documents)) {
209
            $this->searchProvider->addDocuments($this->documents);
210
            $this->documents = [];
211
        }
212
    }
213
214
    /**
215
     * Index a node (including its children) - for the specified language only
216
     *
217
     * @param Node   $node
218
     * @param string $lang
219
     */
220
    public function indexNode(Node $node, $lang)
221
    {
222
        $this->createNodeDocuments($node, $lang);
223
224
        if (!empty($this->documents)) {
225
            $this->searchProvider->addDocuments($this->documents);
226
            $this->documents = [];
227
        }
228
    }
229
230
    /**
231
     * Add documents for the node translation (and children) to the index
232
     *
233
     * @param Node   $node
234
     * @param string $lang
235
     */
236
    public function createNodeDocuments(Node $node, $lang)
237
    {
238
        $nodeTranslation = $node->getNodeTranslation($lang, true);
239
        if ($nodeTranslation && $this->indexNodeTranslation($nodeTranslation)) {
240
            $this->indexChildren($node, $lang);
241
        }
242
    }
243
244
    /**
245
     * Index all children of the specified node (only for the specified
246
     * language)
247
     *
248
     * @param Node   $node
249
     * @param string $lang
250
     */
251
    public function indexChildren(Node $node, $lang)
252
    {
253
        foreach ($node->getChildren() as $childNode) {
254
            $this->indexNode($childNode, $lang);
255
        }
256
    }
257
258
    /**
259
     * Index a node translation
260
     *
261
     * @param NodeTranslation $nodeTranslation
262
     * @param bool            $add             Add node immediately to index?
263
     *
264
     * @return bool Return true if the document has been indexed
265
     */
266
    public function indexNodeTranslation(NodeTranslation $nodeTranslation, $add = false)
267
    {
268
        // Retrieve the public NodeVersion
269
        $publicNodeVersion = $nodeTranslation->getPublicNodeVersion();
270
        if (\is_null($publicNodeVersion)) {
271
            return false;
272
        }
273
274
        $refPage = $this->getNodeRefPage($publicNodeVersion);
275
        if ($refPage->isStructureNode()) {
276
            return true;
277
        }
278
279
        // Only index online NodeTranslations
280
        if (!$nodeTranslation->isOnline()) {
281
            return false;
282
        }
283
284
        $node = $nodeTranslation->getNode();
285
        if ($this->isIndexable($refPage)) {
286
            // Retrieve the referenced entity from the public NodeVersion
287
            $page = $publicNodeVersion->getRef($this->em);
288
289
            $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 287 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...
290
            if ($add) {
291
                $this->searchProvider->addDocuments($this->documents);
292
                $this->documents = [];
293
            }
294
        }
295
296
        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)
297
    }
298
299
    /**
300
     * Return if the page is indexable - by default all pages are indexable,
301
     * you can override this by implementing the IndexableInterface on your
302
     * page entity and returning false in the isIndexable method.
303
     *
304
     * @param HasNodeInterface $page
305
     *
306
     * @return bool
307
     */
308
    protected function isIndexable(HasNodeInterface $page)
309
    {
310
        return $this->container->get('kunstmaan_node.pages_configuration')->isIndexable($page);
311
    }
312
313
    /**
314
     * Remove the specified node translation from the index
315
     *
316
     * @param NodeTranslation $nodeTranslation
317
     */
318
    public function deleteNodeTranslation(NodeTranslation $nodeTranslation)
319
    {
320
        $uid = 'nodetranslation_' . $nodeTranslation->getId();
321
        $indexName = $this->indexName . '_' . $nodeTranslation->getLang();
322
        $this->searchProvider->deleteDocument($indexName, $this->indexType, $uid);
323
    }
324
325
    /**
326
     * Delete the specified index
327
     */
328
    public function deleteIndex()
329
    {
330
        foreach ($this->locales as $locale) {
331
            $this->searchProvider->deleteIndex($this->indexName . '_' . $locale);
332
        }
333
    }
334
335
    /**
336
     * Apply the analysis factory to the index
337
     *
338
     * @param Index                    $index
339
     * @param AnalysisFactoryInterface $analysis
340
     */
341
    public function setAnalysis(Index $index, AnalysisFactoryInterface $analysis)
342
    {
343
        $analysers = $analysis->build();
344
        $args = [
345
            'number_of_shards' => $this->numberOfShards,
346
            'number_of_replicas' => $this->numberOfReplicas,
347
            'analysis' => $analysers,
348
        ];
349
350
        if (class_exists(\Elastica\Mapping::class)) {
351
            $args = [
352
                'settings' => [
353
                    'number_of_shards' => $this->numberOfShards,
354
                    'number_of_replicas' => $this->numberOfReplicas,
355
                    'analysis' => $analysers,
356
                ],
357
            ];
358
359
            $ngramDiff = 1;
360
            if (isset($analysers['tokenizer']) && count($analysers['tokenizer']) > 0) {
361
                foreach ($analysers['tokenizer'] as $tokenizer) {
362
                    if ($tokenizer['type'] === 'nGram') {
363
                        $diff = $tokenizer['max_gram'] - $tokenizer['min_gram'];
364
365
                        $ngramDiff = $diff > $ngramDiff ? $diff : $ngramDiff;
366
                    }
367
                }
368
            }
369
370
            if ($ngramDiff > 1) {
371
                $args['settings']['max_ngram_diff'] = $ngramDiff;
372
            }
373
        }
374
375
        $index->create($args);
376
    }
377
378
    /**
379
     * Return default search fields mapping for node translations
380
     *
381
     * @param Index  $index
382
     * @param string $lang
383
     *
384
     * @return Mapping|\Elastica\Mapping
385
     */
386
    protected function createDefaultSearchFieldsMapping(Index $index, $lang = 'en')
387
    {
388
        if (class_exists(\Elastica\Type\Mapping::class)) {
389
            $mapping = new Mapping();
390
            $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...
391
        } else {
392
            $mapping = new \Elastica\Mapping();
393
        }
394
395
        $mapping->setProperties($this->properties);
396
397
        return $mapping;
398
    }
399
400
    /**
401
     * Initialize the index with the default search fields mapping
402
     *
403
     * @param Index  $index
404
     * @param string $lang
405
     */
406
    protected function setMapping(Index $index, $lang = 'en')
407
    {
408
        $mapping = $this->createDefaultSearchFieldsMapping($index, $lang);
409
        if (class_exists(\Elastica\Mapping::class)) {
410
            $mapping->send($index);
411
        } else {
412
            $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...
413
        }
414
        $index->refresh();
415
    }
416
417
    /**
418
     * Create a search document for a page
419
     *
420
     * @param NodeTranslation  $nodeTranslation
421
     * @param Node             $node
422
     * @param NodeVersion      $publicNodeVersion
423
     * @param HasNodeInterface $page
424
     */
425
    protected function addPageToIndex(
426
        NodeTranslation $nodeTranslation,
427
        Node $node,
428
        NodeVersion $publicNodeVersion,
429
        HasNodeInterface $page
430
    ) {
431
        $rootNode = $this->currentTopNode;
432
        if (!$rootNode) {
433
            // Fetch main parent of current node...
434
            $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...
435
                $node,
436
                $nodeTranslation->getLang()
437
            );
438
        }
439
440
        $doc = array(
441
            'root_id' => $rootNode->getId(),
442
            'node_id' => $node->getId(),
443
            'node_translation_id' => $nodeTranslation->getId(),
444
            'node_version_id' => $publicNodeVersion->getId(),
445
            'title' => $nodeTranslation->getTitle(),
446
            'slug' => $nodeTranslation->getFullSlug(),
447
            'page_class' => ClassLookup::getClass($page),
448
            'created' => $this->getUTCDateTime(
449
                $nodeTranslation->getCreated()
450
            )->format(\DateTime::ISO8601),
451
            'updated' => $this->getUTCDateTime(
452
                $nodeTranslation->getUpdated()
453
            )->format(\DateTime::ISO8601),
454
        );
455
        if ($this->logger) {
456
            $this->logger->info('Indexing document : ' . implode(', ', $doc));
457
        }
458
459
        // Permissions
460
        $this->addPermissions($node, $doc);
461
462
        // Search type
463
        $this->addSearchType($page, $doc);
464
465
        // Parent and Ancestors
466
        $this->addParentAndAncestors($node, $doc);
467
468
        // Content
469
        $this->addPageContent($nodeTranslation, $page, $doc);
470
471
        // Add document to index
472
        $uid = 'nodetranslation_' . $nodeTranslation->getId();
473
474
        $this->addCustomData($page, $doc);
475
476
        $this->documents[] = $this->searchProvider->createDocument(
477
            $uid,
478
            $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...
479
            $this->indexName . '_' . $nodeTranslation->getLang(),
480
            $this->indexType
481
        );
482
    }
483
484
    /**
485
     * Add view permissions to the index document
486
     *
487
     * @param Node  $node
488
     * @param array $doc
489
     *
490
     * @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...
491
     */
492
    protected function addPermissions(Node $node, &$doc)
493
    {
494
        if (!\is_null($this->aclProvider)) {
495
            $roles = $this->getAclPermissions($node);
496
        } else {
497
            // Fallback when no ACL available / assume everything is accessible...
498
            $roles = array('IS_AUTHENTICATED_ANONYMOUSLY');
499
        }
500
        $doc['view_roles'] = $roles;
501
    }
502
503
    /**
504
     * Add type to the index document
505
     *
506
     * @param object $page
507
     * @param array  $doc
508
     *
509
     * @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...
510
     */
511
    protected function addSearchType($page, &$doc)
512
    {
513
        $doc['type'] = $this->container->get('kunstmaan_node.pages_configuration')->getSearchType($page);
514
    }
515
516
    /**
517
     * Add parent nodes to the index document
518
     *
519
     * @param Node  $node
520
     * @param array $doc
521
     *
522
     * @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...
523
     */
524
    protected function addParentAndAncestors($node, &$doc)
525
    {
526
        $parent = $node->getParent();
527
528
        if ($parent) {
529
            $doc['parent'] = $parent->getId();
530
            $ancestors = [];
531
            do {
532
                $ancestors[] = $parent->getId();
533
                $parent = $parent->getParent();
534
            } while ($parent);
535
            $doc['ancestors'] = $ancestors;
536
        }
537
    }
538
539
    /**
540
     * Add page content to the index document
541
     *
542
     * @param NodeTranslation  $nodeTranslation
543
     * @param HasNodeInterface $page
544
     * @param array            $doc
545
     */
546
    protected function addPageContent(NodeTranslation $nodeTranslation, $page, &$doc)
547
    {
548
        $this->enterRequestScope($nodeTranslation->getLang());
549
        if ($this->logger) {
550
            $this->logger->debug(
551
                sprintf(
552
                    'Indexing page "%s" / lang : %s / type : %s / id : %d / node id : %d',
553
                    $page->getTitle(),
554
                    $nodeTranslation->getLang(),
555
                    \get_class($page),
556
                    $page->getId(),
557
                    $nodeTranslation->getNode()->getId()
558
                )
559
            );
560
        }
561
562
        $doc['content'] = '';
563 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...
564
            if ($this->isMethodOverridden('renderCustomSearchView')) {
565
                @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...
566
567
                $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...
568
            } else {
569
                $searchViewRenderer = $this->container->get('kunstmaan_node_search.service.search_view_renderer');
570
571
                $doc['content'] = $searchViewRenderer->renderCustomSearchView($nodeTranslation, $page, $this->container);
572
            }
573
574
            return null;
575
        }
576
577 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...
578
            if ($this->isMethodOverridden('renderDefaultSearchView')) {
579
                @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...
580
581
                $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...
582
            } else {
583
                $searchViewRenderer = $this->container->get('kunstmaan_node_search.service.search_view_renderer');
584
585
                $doc['content'] = $searchViewRenderer->renderDefaultSearchView($nodeTranslation, $page);
586
            }
587
588
            return null;
589
        }
590
    }
591
592
    /**
593
     * Enter request scope if it is not active yet...
594
     *
595
     * @param string $lang
596
     */
597
    protected function enterRequestScope($lang)
598
    {
599
        $locale = null;
600
        $requestStack = $this->container->get('request_stack');
601
        // If there already is a request, get the locale from it.
602
        if ($requestStack->getCurrentRequest()) {
603
            $locale = $requestStack->getCurrentRequest()->getLocale();
604
        }
605
        // If we don't have a request or the current request locale is different from the node langauge
606
        if (!$requestStack->getCurrentRequest() || ($locale && $locale !== $lang)) {
607
            $request = new Request();
608
            $request->setLocale($lang);
609
610
            $context = $this->container->get('router')->getContext();
611
            $context->setParameter('_locale', $lang);
612
613
            $requestStack->push($request);
614
        }
615
    }
616
617
    /**
618
     * Render a custom search view
619
     *
620
     * @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.
621
     *
622
     * @param NodeTranslation             $nodeTranslation
623
     * @param SearchViewTemplateInterface $page
624
     * @param EngineInterface             $renderer
625
     *
626
     * @return string
627
     */
628
    protected function renderCustomSearchView(
629
        NodeTranslation $nodeTranslation,
630
        SearchViewTemplateInterface $page,
631
        EngineInterface $renderer
632
    ) {
633
        @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...
634
635
        $view = $page->getSearchView();
636
        $renderContext = new RenderContext([
637
            'locale' => $nodeTranslation->getLang(),
638
            'page' => $page,
639
            'indexMode' => true,
640
            'nodetranslation' => $nodeTranslation,
641
        ]);
642
643
        if ($page instanceof PageInterface) {
644
            $request = $this->container->get('request_stack')->getCurrentRequest();
645
            $page->service($this->container, $request, $renderContext);
646
        }
647
648
        $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...
649
            $renderer->render(
650
                $view,
651
                $renderContext->getArrayCopy()
652
            )
653
        );
654
655
        return $content;
656
    }
657
658
    /**
659
     * Render default search view (all indexable pageparts in the main context
660
     * of the page)
661
     *
662
     * @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.
663
     *
664
     * @param NodeTranslation       $nodeTranslation
665
     * @param HasPagePartsInterface $page
666
     * @param EngineInterface       $renderer
667
     *
668
     * @return string
669
     */
670
    protected function renderDefaultSearchView(
671
        NodeTranslation $nodeTranslation,
672
        HasPagePartsInterface $page,
673
        EngineInterface $renderer
674
    ) {
675
        @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...
676
677
        $pageparts = $this->indexablePagePartsService->getIndexablePageParts($page);
678
        $view = '@KunstmaanNodeSearch/PagePart/view.html.twig';
679
        $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...
680
            $renderer->render(
681
                $view,
682
                array(
683
                    'locale' => $nodeTranslation->getLang(),
684
                    'page' => $page,
685
                    'pageparts' => $pageparts,
686
                    'indexMode' => true,
687
                )
688
            )
689
        );
690
691
        return $content;
692
    }
693
694
    /**
695
     * Add custom data to index document (you can override to add custom fields
696
     * to the search index)
697
     *
698
     * @param HasNodeInterface $page
699
     * @param array            $doc
700
     */
701
    protected function addCustomData(HasNodeInterface $page, &$doc)
702
    {
703
        $event = new IndexNodeEvent($page, $doc);
704
        $this->container->get('event_dispatcher')->dispatch(IndexNodeEvent::EVENT_INDEX_NODE, $event);
705
706
        $doc = $event->doc;
707
708
        if ($page instanceof HasCustomSearchDataInterface) {
709
            $doc += $page->getCustomSearchData($doc);
710
        }
711
    }
712
713
    /**
714
     * Convert a DateTime to UTC equivalent...
715
     *
716
     * @param \DateTime $dateTime
717
     *
718
     * @return \DateTime
719
     */
720
    protected function getUTCDateTime(\DateTime $dateTime)
721
    {
722
        $result = clone $dateTime;
723
        $result->setTimezone(new \DateTimeZone('UTC'));
724
725
        return $result;
726
    }
727
728
    /**
729
     * Removes all HTML markup & decode HTML entities
730
     *
731
     * @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.
732
     *
733
     * @param $text
734
     *
735
     * @return string
736
     */
737
    protected function removeHtml($text)
738
    {
739
        @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...
740
741
        $searchViewRenderer = $this->container->get('kunstmaan_node_search.service.search_view_renderer');
742
743
        return $searchViewRenderer->removeHtml($text);
744
    }
745
746
    /**
747
     * Fetch ACL permissions for the specified entity
748
     *
749
     * @param object $object
750
     *
751
     * @return array
752
     */
753
    protected function getAclPermissions($object)
754
    {
755
        $roles = [];
756
757
        try {
758
            $objectIdentity = ObjectIdentity::fromDomainObject($object);
759
760
            /* @var AclInterface $acl */
761
            $acl = $this->aclProvider->findAcl($objectIdentity);
762
            $objectAces = $acl->getObjectAces();
763
764
            /* @var AuditableEntryInterface $ace */
765 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...
766
                $securityIdentity = $ace->getSecurityIdentity();
767
                if (
768
                    $securityIdentity instanceof RoleSecurityIdentity &&
769
                    ($ace->getMask() & MaskBuilder::MASK_VIEW != 0)
770
                ) {
771
                    $roles[] = $securityIdentity->getRole();
772
                }
773
            }
774
        } catch (AclNotFoundException $e) {
775
            // No ACL found... assume default
776
            $roles = array('IS_AUTHENTICATED_ANONYMOUSLY');
777
        }
778
779
        return $roles;
780
    }
781
782
    /**
783
     * @param $publicNodeVersion
784
     *
785
     * @return mixed
786
     */
787
    private function getNodeRefPage(NodeVersion $publicNodeVersion)
788
    {
789
        $refEntityName = $publicNodeVersion->getRefEntityName();
790
791
        if (!isset($this->nodeRefs[$refEntityName])) {
792
            $this->nodeRefs[$refEntityName] = new $refEntityName();
793
        }
794
795
        return $this->nodeRefs[$refEntityName];
796
    }
797
798
    private function isMethodOverridden(string $method)
799
    {
800
        $reflector = new \ReflectionMethod($this, $method);
801
802
        return $reflector->getDeclaringClass()->getName() !== __CLASS__;
803
    }
804
}
805