Completed
Pull Request — master (#2657)
by Jeroen
06:22
created

NodePagesConfiguration::getEventDispatcher()   A

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
c 0
b 0
f 0
ccs 0
cts 0
cp 0
rs 10
cc 1
nc 1
nop 0
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\Event\Events;
16
use Kunstmaan\NodeBundle\Event\PageRenderEvent;
17
use Kunstmaan\NodeBundle\Helper\RenderContext;
18
use Kunstmaan\NodeSearchBundle\Event\IndexNodeEvent;
19
use Kunstmaan\NodeSearchBundle\Helper\IndexablePagePartsService;
20
use Kunstmaan\NodeSearchBundle\Helper\SearchViewTemplateInterface;
21
use Kunstmaan\PagePartBundle\Helper\HasPagePartsInterface;
22
use Kunstmaan\SearchBundle\Configuration\SearchConfigurationInterface;
23
use Kunstmaan\SearchBundle\Provider\SearchProviderInterface;
24
use Kunstmaan\SearchBundle\Search\AnalysisFactoryInterface;
25
use Kunstmaan\UtilitiesBundle\Helper\ClassLookup;
26
use Psr\Log\LoggerInterface;
27
use Symfony\Component\DependencyInjection\ContainerInterface;
28
use Symfony\Component\DomCrawler\Crawler;
29
use Symfony\Component\HttpFoundation\Request;
30
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
31
use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
32
use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
33
use Symfony\Component\Security\Acl\Model\AclInterface;
34
use Symfony\Component\Security\Acl\Model\AclProviderInterface;
35
use Symfony\Component\Security\Acl\Model\AuditableEntryInterface;
36
use Symfony\Component\Templating\EngineInterface;
37
38
class NodePagesConfiguration implements SearchConfigurationInterface
39
{
40
    /** @var string */
41
    protected $indexName;
42
43
    /** @var string */
44
    protected $indexType;
45
46
    /** @var SearchProviderInterface */
47
    protected $searchProvider;
48
49
    /** @var array */
50
    protected $locales = [];
51
52
    /** @var array */
53
    protected $analyzerLanguages;
54
55
    /** @var EntityManager */
56
    protected $em;
57
58
    /** @var array */
59
    protected $documents = [];
60
61
    /** @var ContainerInterface */
62
    protected $container;
63
64
    /** @var AclProviderInterface */
65
    protected $aclProvider = null;
66
67
    /** @var LoggerInterface */
68
    protected $logger = null;
69
70
    /** @var IndexablePagePartsService */
71
    protected $indexablePagePartsService;
72
73
    /** @var DomainConfigurationInterface */
74
    protected $domainConfiguration;
75
76
    /** @var array */
77
    protected $properties = [];
78
79
    /** @var int */
80
    protected $numberOfShards;
81
82
    /** @var int */
83
    protected $numberOfReplicas;
84
85
    /** @var Node */
86
    protected $currentTopNode = null;
87
88
    /** @var array */
89
    protected $nodeRefs = [];
90
91
    /**
92
     * @param ContainerInterface      $container
93
     * @param SearchProviderInterface $searchProvider
94
     * @param string                  $name
95
     * @param string                  $type
96
     */
97
    public function __construct($container, $searchProvider, $name, $type, $numberOfShards = 1, $numberOfReplicas = 0)
98
    {
99
        $this->container = $container;
100
        $this->indexName = $name;
101
        $this->indexType = $type;
102
        $this->searchProvider = $searchProvider;
103
        $this->domainConfiguration = $this->container->get('kunstmaan_admin.domain_configuration');
104
        $this->locales = $this->domainConfiguration->getBackendLocales();
105
        $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...
106
        $this->em = $this->container->get('doctrine')->getManager();
107
        $this->numberOfShards = $numberOfShards;
108
        $this->numberOfReplicas = $numberOfReplicas;
109
    }
110
111
    /**
112
     * @param AclProviderInterface $aclProvider
113
     */
114
    public function setAclProvider(AclProviderInterface $aclProvider)
115
    {
116
        $this->aclProvider = $aclProvider;
117
    }
118
119
    /**
120
     * @param IndexablePagePartsService $indexablePagePartsService
121
     */
122
    public function setIndexablePagePartsService(IndexablePagePartsService $indexablePagePartsService)
123
    {
124
        $this->indexablePagePartsService = $indexablePagePartsService;
125
    }
126
127
    /**
128
     * @param array $properties
129
     */
130
    public function setDefaultProperties(array $properties)
131
    {
132
        $this->properties = array_merge($this->properties, $properties);
133
    }
134
135
    /**
136
     * @param LoggerInterface $logger
137
     */
138
    public function setLogger(LoggerInterface $logger)
139
    {
140
        $this->logger = $logger;
141
    }
142
143
    /**
144
     * @return array
145
     */
146
    public function getLanguagesNotAnalyzed()
147
    {
148
        $notAnalyzed = [];
149
        foreach ($this->locales as $locale) {
150
            if (preg_match('/[a-z]{2}_?+[a-zA-Z]{2}/', $locale)) {
151
                $locale = strtolower($locale);
152
            }
153
154
            if (false === \array_key_exists($locale, $this->analyzerLanguages)) {
155
                $notAnalyzed[] = $locale;
156
            }
157
        }
158
159
        return $notAnalyzed;
160
    }
161
162
    /**
163
     * Create node index
164
     */
165
    public function createIndex()
166
    {
167
        //create analysis
168
        $analysis = $this->container->get(
169
            'kunstmaan_search.search.factory.analysis'
170
        );
171
172
        foreach ($this->locales as $locale) {
173
            // Multilanguage check
174
            if (preg_match('/[a-z]{2}_?+[a-zA-Z]{2}/', $locale)) {
175
                $locale = strtolower($locale);
176
            }
177
178
            // Build new index
179
            $index = $this->searchProvider->createIndex($this->indexName . '_' . $locale);
180
181
            if (\array_key_exists($locale, $this->analyzerLanguages)) {
182
                $localeAnalysis = clone $analysis;
183
                $language = $this->analyzerLanguages[$locale]['analyzer'];
184
185
                // Create index with analysis
186
                $this->setAnalysis($index, $localeAnalysis->setupLanguage($language));
187
            } else {
188
                $index->create();
189
            }
190
191
            $this->setMapping($index, $locale);
192
        }
193
    }
194
195
    /**
196
     * Populate node index
197
     */
198
    public function populateIndex()
199
    {
200
        $nodeRepository = $this->em->getRepository('KunstmaanNodeBundle:Node');
201
        $nodes = $nodeRepository->getAllTopNodes();
202
203
        foreach ($nodes as $node) {
204
            $this->currentTopNode = $node;
205
            foreach ($this->locales as $lang) {
206
                $this->createNodeDocuments($node, $lang);
207
            }
208
        }
209
210
        if (!empty($this->documents)) {
211
            $this->searchProvider->addDocuments($this->documents);
212
            $this->documents = [];
213
        }
214
    }
215
216
    /**
217
     * Index a node (including its children) - for the specified language only
218
     *
219
     * @param Node   $node
220
     * @param string $lang
221
     */
222
    public function indexNode(Node $node, $lang)
223
    {
224
        $this->createNodeDocuments($node, $lang);
225
226
        if (!empty($this->documents)) {
227
            $this->searchProvider->addDocuments($this->documents);
228
            $this->documents = [];
229
        }
230
    }
231
232
    /**
233
     * Add documents for the node translation (and children) to the index
234
     *
235
     * @param Node   $node
236
     * @param string $lang
237
     */
238
    public function createNodeDocuments(Node $node, $lang)
239
    {
240
        $nodeTranslation = $node->getNodeTranslation($lang, true);
241
        if ($nodeTranslation && $this->indexNodeTranslation($nodeTranslation)) {
242
            $this->indexChildren($node, $lang);
243
        }
244
    }
245
246
    /**
247
     * Index all children of the specified node (only for the specified
248
     * language)
249
     *
250
     * @param Node   $node
251
     * @param string $lang
252
     */
253
    public function indexChildren(Node $node, $lang)
254
    {
255
        foreach ($node->getChildren() as $childNode) {
256
            $this->indexNode($childNode, $lang);
257
        }
258
    }
259
260
    /**
261
     * Index a node translation
262
     *
263
     * @param NodeTranslation $nodeTranslation
264
     * @param bool            $add             Add node immediately to index?
265
     *
266
     * @return bool Return true if the document has been indexed
267
     */
268
    public function indexNodeTranslation(NodeTranslation $nodeTranslation, $add = false)
269
    {
270
        // Retrieve the public NodeVersion
271
        $publicNodeVersion = $nodeTranslation->getPublicNodeVersion();
272
        if (\is_null($publicNodeVersion)) {
273
            return false;
274
        }
275
276
        $refPage = $this->getNodeRefPage($publicNodeVersion);
277
        if ($refPage->isStructureNode()) {
278
            return true;
279
        }
280
281
        // Only index online NodeTranslations
282
        if (!$nodeTranslation->isOnline()) {
283
            return false;
284
        }
285
286
        $node = $nodeTranslation->getNode();
287
        if ($this->isIndexable($refPage)) {
288
            // Retrieve the referenced entity from the public NodeVersion
289
            $page = $publicNodeVersion->getRef($this->em);
290
291
            $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 289 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...
292
            if ($add) {
293
                $this->searchProvider->addDocuments($this->documents);
294
                $this->documents = [];
295
            }
296
        }
297
298
        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)
299
    }
300
301
    /**
302
     * Return if the page is indexable - by default all pages are indexable,
303
     * you can override this by implementing the IndexableInterface on your
304
     * page entity and returning false in the isIndexable method.
305
     *
306
     * @param HasNodeInterface $page
307
     *
308
     * @return bool
309
     */
310
    protected function isIndexable(HasNodeInterface $page)
311
    {
312
        return $this->container->get('kunstmaan_node.pages_configuration')->isIndexable($page);
313
    }
314
315
    /**
316
     * Remove the specified node translation from the index
317
     *
318
     * @param NodeTranslation $nodeTranslation
319
     */
320
    public function deleteNodeTranslation(NodeTranslation $nodeTranslation)
321
    {
322
        $uid = 'nodetranslation_' . $nodeTranslation->getId();
323
        $indexName = $this->indexName . '_' . $nodeTranslation->getLang();
324
        $this->searchProvider->deleteDocument($indexName, $this->indexType, $uid);
325
    }
326
327
    /**
328
     * Delete the specified index
329
     */
330
    public function deleteIndex()
331
    {
332
        foreach ($this->locales as $locale) {
333
            $this->searchProvider->deleteIndex($this->indexName . '_' . $locale);
334
        }
335
    }
336
337
    /**
338
     * Apply the analysis factory to the index
339
     *
340
     * @param Index                    $index
341
     * @param AnalysisFactoryInterface $analysis
342
     */
343
    public function setAnalysis(Index $index, AnalysisFactoryInterface $analysis)
344
    {
345
        $index->create(
346
            array(
347
                'number_of_shards' => $this->numberOfShards,
348
                'number_of_replicas' => $this->numberOfReplicas,
349
                'analysis' => $analysis->build(),
350
            )
351
        );
352
    }
353
354
    /**
355
     * Return default search fields mapping for node translations
356
     *
357
     * @param Index  $index
358
     * @param string $lang
359
     *
360
     * @return Mapping
361
     */
362
    protected function createDefaultSearchFieldsMapping(Index $index, $lang = 'en')
363
    {
364
        $mapping = new Mapping();
365
        $mapping->setType($index->getType($this->indexType));
366
367
        $mapping->setProperties($this->properties);
368
369
        return $mapping;
370
    }
371
372
    /**
373
     * Initialize the index with the default search fields mapping
374
     *
375
     * @param Index  $index
376
     * @param string $lang
377
     */
378
    protected function setMapping(Index $index, $lang = 'en')
379
    {
380
        $mapping = $this->createDefaultSearchFieldsMapping($index, $lang);
381
        $mapping->send();
382
        $index->refresh();
383
    }
384
385
    /**
386
     * Create a search document for a page
387
     *
388
     * @param NodeTranslation  $nodeTranslation
389
     * @param Node             $node
390
     * @param NodeVersion      $publicNodeVersion
391
     * @param HasNodeInterface $page
392
     */
393
    protected function addPageToIndex(
394
        NodeTranslation $nodeTranslation,
395
        Node $node,
396
        NodeVersion $publicNodeVersion,
397
        HasNodeInterface $page
398
    ) {
399
        $rootNode = $this->currentTopNode;
400
        if (!$rootNode) {
401
            // Fetch main parent of current node...
402
            $rootNode = $this->em->getRepository('KunstmaanNodeBundle:Node')->getRootNodeFor(
403
                $node,
404
                $nodeTranslation->getLang()
405
            );
406
        }
407
408
        $doc = array(
409
            'root_id' => $rootNode->getId(),
410
            'node_id' => $node->getId(),
411
            'node_translation_id' => $nodeTranslation->getId(),
412
            'node_version_id' => $publicNodeVersion->getId(),
413
            'title' => $nodeTranslation->getTitle(),
414
            'slug' => $nodeTranslation->getFullSlug(),
415
            'page_class' => ClassLookup::getClass($page),
416
            'created' => $this->getUTCDateTime(
417
                $nodeTranslation->getCreated()
418
            )->format(\DateTime::ISO8601),
419
            'updated' => $this->getUTCDateTime(
420
                $nodeTranslation->getUpdated()
421
            )->format(\DateTime::ISO8601),
422
        );
423
        if ($this->logger) {
424
            $this->logger->info('Indexing document : ' . implode(', ', $doc));
425
        }
426
427
        // Permissions
428
        $this->addPermissions($node, $doc);
429
430
        // Search type
431
        $this->addSearchType($page, $doc);
432
433
        // Parent and Ancestors
434
        $this->addParentAndAncestors($node, $doc);
435
436
        // Content
437
        $this->addPageContent($nodeTranslation, $page, $doc);
438
439
        // Add document to index
440
        $uid = 'nodetranslation_' . $nodeTranslation->getId();
441
442
        $this->addCustomData($page, $doc);
443
444
        $this->documents[] = $this->searchProvider->createDocument(
445
            $uid,
446
            $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...
447
            $this->indexName . '_' . $nodeTranslation->getLang(),
448
            $this->indexType
449
        );
450
    }
451
452
    /**
453
     * Add view permissions to the index document
454
     *
455
     * @param Node  $node
456
     * @param array $doc
457
     *
458
     * @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...
459
     */
460
    protected function addPermissions(Node $node, &$doc)
461
    {
462
        if (!\is_null($this->aclProvider)) {
463
            $roles = $this->getAclPermissions($node);
464
        } else {
465
            // Fallback when no ACL available / assume everything is accessible...
466
            $roles = array('IS_AUTHENTICATED_ANONYMOUSLY');
467
        }
468
        $doc['view_roles'] = $roles;
469
    }
470
471
    /**
472
     * Add type to the index document
473
     *
474
     * @param object $page
475
     * @param array  $doc
476
     *
477
     * @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...
478
     */
479
    protected function addSearchType($page, &$doc)
480
    {
481
        $doc['type'] = $this->container->get('kunstmaan_node.pages_configuration')->getSearchType($page);
482
    }
483
484
    /**
485
     * Add parent nodes 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 addParentAndAncestors($node, &$doc)
493
    {
494
        $parent = $node->getParent();
495
496
        if ($parent) {
497
            $doc['parent'] = $parent->getId();
498
            $ancestors = [];
499
            do {
500
                $ancestors[] = $parent->getId();
501
                $parent = $parent->getParent();
502
            } while ($parent);
503
            $doc['ancestors'] = $ancestors;
504
        }
505
    }
506
507
    /**
508
     * Add page content to the index document
509
     *
510
     * @param NodeTranslation  $nodeTranslation
511
     * @param HasNodeInterface $page
512
     * @param array            $doc
513
     */
514
    protected function addPageContent(NodeTranslation $nodeTranslation, $page, &$doc)
515
    {
516
        $this->enterRequestScope($nodeTranslation->getLang());
517
        if ($this->logger) {
518
            $this->logger->debug(
519
                sprintf(
520
                    'Indexing page "%s" / lang : %s / type : %s / id : %d / node id : %d',
521
                    $page->getTitle(),
522
                    $nodeTranslation->getLang(),
523
                    \get_class($page),
524
                    $page->getId(),
525
                    $nodeTranslation->getNode()->getId()
526
                )
527
            );
528
        }
529
530
        $renderer = $this->container->get('templating');
531
        $doc['content'] = '';
532
533
        if ($page instanceof SearchViewTemplateInterface) {
534
            $doc['content'] = $this->renderCustomSearchView($nodeTranslation, $page, $renderer);
0 ignored issues
show
Documentation introduced by
$renderer 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...
535
536
            return null;
537
        }
538
539
        if ($page instanceof HasPagePartsInterface) {
540
            $doc['content'] = $this->renderDefaultSearchView($nodeTranslation, $page, $renderer);
0 ignored issues
show
Documentation introduced by
$renderer 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...
541
542
            return null;
543
        }
544
    }
545
546
    /**
547
     * Enter request scope if it is not active yet...
548
     *
549
     * @param string $lang
550
     */
551
    protected function enterRequestScope($lang)
552
    {
553
        $requestStack = $this->container->get('request_stack');
554
        // If there already is a request, get the locale from it.
555
        if ($requestStack->getCurrentRequest()) {
556
            $locale = $requestStack->getCurrentRequest()->getLocale();
557
        }
558
        // If we don't have a request or the current request locale is different from the node langauge
559
        if (!$requestStack->getCurrentRequest() || ($locale && $locale !== $lang)) {
0 ignored issues
show
Bug introduced by
The variable $locale does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
560
            $request = new Request();
561
            $request->setLocale($lang);
562
563
            $context = $this->container->get('router')->getContext();
564
            $context->setParameter('_locale', $lang);
565
566
            $requestStack->push($request);
567
        }
568
    }
569
570
    /**
571
     * Render a custom search view
572
     *
573
     * @param NodeTranslation             $nodeTranslation
574
     * @param SearchViewTemplateInterface $page
575
     * @param EngineInterface             $renderer
576
     *
577
     * @return string
578
     */
579
    protected function renderCustomSearchView(
580
        NodeTranslation $nodeTranslation,
581
        SearchViewTemplateInterface $page,
582
        EngineInterface $renderer
583
    ) {
584
        $view = $page->getSearchView();
585
        $renderContext = new RenderContext([
586
            'locale' => $nodeTranslation->getLang(),
587
            'page' => $page,
588
            'indexMode' => true,
589
            'nodetranslation' => $nodeTranslation,
590
        ]);
591
592
        $request = $this->container->get('request_stack')->getCurrentRequest();
593
        // NEXT_MAJOR: Remove if and `$page->service` call
594
        if ($page instanceof PageInterface) {
595
            $page->service($this->container, $request, $renderContext);
596
        }
597
598
        $pageRenderEvent = new PageRenderEvent($request, $page, $renderContext);
0 ignored issues
show
Documentation introduced by
$page is of type object<Kunstmaan\NodeSea...hViewTemplateInterface>, but the function expects a object<Kunstmaan\NodeBundle\Entity\PageInterface>.

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...
599
        $this->getEventDispatcher()->dispatch(Events::PAGE_RENDER, $pageRenderEvent);
600
601
        $renderContext = clone $pageRenderEvent->getRenderContext();
602
603
        $content = $this->removeHtml(
604
            $renderer->render(
605
                $view,
606
                $renderContext->getArrayCopy()
607
            )
608
        );
609
610
        return $content;
611
    }
612
613
    /**
614
     * Render default search view (all indexable pageparts in the main context
615
     * of the page)
616
     *
617
     * @param NodeTranslation       $nodeTranslation
618
     * @param HasPagePartsInterface $page
619
     * @param EngineInterface       $renderer
620
     *
621
     * @return string
622
     */
623
    protected function renderDefaultSearchView(
624
        NodeTranslation $nodeTranslation,
625
        HasPagePartsInterface $page,
626
        EngineInterface $renderer
627
    ) {
628
        $pageparts = $this->indexablePagePartsService->getIndexablePageParts($page);
629
        $view = '@KunstmaanNodeSearch/PagePart/view.html.twig';
630
        $content = $this->removeHtml(
631
            $renderer->render(
632
                $view,
633
                array(
634
                    'locale' => $nodeTranslation->getLang(),
635
                    'page' => $page,
636
                    'pageparts' => $pageparts,
637
                    'indexMode' => true,
638
                )
639
            )
640
        );
641
642
        return $content;
643
    }
644
645
    /**
646
     * Add custom data to index document (you can override to add custom fields
647
     * to the search index)
648
     *
649
     * @param HasNodeInterface $page
650
     * @param array            $doc
651
     */
652
    protected function addCustomData(HasNodeInterface $page, &$doc)
653
    {
654
        $event = new IndexNodeEvent($page, $doc);
655
        $this->getEventDispatcher()->dispatch(IndexNodeEvent::EVENT_INDEX_NODE, $event);
656
657
        $doc = $event->doc;
658
659
        if ($page instanceof HasCustomSearchDataInterface) {
660
            $doc += $page->getCustomSearchData($doc);
661
        }
662
    }
663
664
    /**
665
     * Convert a DateTime to UTC equivalent...
666
     *
667
     * @param \DateTime $dateTime
668
     *
669
     * @return \DateTime
670
     */
671
    protected function getUTCDateTime(\DateTime $dateTime)
672
    {
673
        $result = clone $dateTime;
674
        $result->setTimezone(new \DateTimeZone('UTC'));
675
676
        return $result;
677
    }
678
679
    /**
680
     * Removes all HTML markup & decode HTML entities
681
     *
682
     * @param $text
683
     *
684
     * @return string
685
     */
686
    protected function removeHtml($text)
687
    {
688
        if (!trim($text)) {
689
            return '';
690
        }
691
692
        // Remove Styles and Scripts
693
        $crawler = new Crawler();
694
        $crawler->addHtmlContent($text);
695
        $crawler->filter('style, script')->each(function (Crawler $crawler) {
696
            foreach ($crawler as $node) {
697
                $node->parentNode->removeChild($node);
698
            }
699
        });
700
        $text = $crawler->html();
701
702
        // Remove HTML markup
703
        $result = strip_tags($text);
704
705
        // Decode HTML entities
706
        $result = trim(html_entity_decode($result, ENT_QUOTES));
707
708
        return $result;
709
    }
710
711
    /**
712
     * Fetch ACL permissions for the specified entity
713
     *
714
     * @param object $object
715
     *
716
     * @return array
717
     */
718
    protected function getAclPermissions($object)
719
    {
720
        $roles = [];
721
722
        try {
723
            $objectIdentity = ObjectIdentity::fromDomainObject($object);
724
725
            /* @var AclInterface $acl */
726
            $acl = $this->aclProvider->findAcl($objectIdentity);
727
            $objectAces = $acl->getObjectAces();
728
729
            /* @var AuditableEntryInterface $ace */
730 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...
731
                $securityIdentity = $ace->getSecurityIdentity();
732
                if (
733
                    $securityIdentity instanceof RoleSecurityIdentity &&
734
                    ($ace->getMask() & MaskBuilder::MASK_VIEW != 0)
735
                ) {
736
                    $roles[] = $securityIdentity->getRole();
737
                }
738
            }
739
        } catch (AclNotFoundException $e) {
740
            // No ACL found... assume default
741
            $roles = array('IS_AUTHENTICATED_ANONYMOUSLY');
742
        }
743
744
        return $roles;
745
    }
746
747
    /**
748
     * @param $publicNodeVersion
749
     *
750
     * @return mixed
751
     */
752
    private function getNodeRefPage(NodeVersion $publicNodeVersion)
753
    {
754
        $refEntityName = $publicNodeVersion->getRefEntityName();
755
756
        if (!isset($this->nodeRefs[$refEntityName])) {
757
            $this->nodeRefs[$refEntityName] = new $refEntityName();
758
        }
759
760
        return $this->nodeRefs[$refEntityName];
761
    }
762
763
    private function getEventDispatcher()
764
    {
765
        return $this->container->get('event_dispatcher');
766
    }
767
}
768