Completed
Push — master ( 6d6774...64f3ed )
by Jeroen
11:23 queued 05:13
created

Configuration/NodePagesConfiguration.php (1 issue)

Upgrade to new PHP Analysis Engine

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

1
<?php
2
3
namespace Kunstmaan\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\PagePartBundle\Helper\HasPagePartsInterface;
20
use Kunstmaan\SearchBundle\Configuration\SearchConfigurationInterface;
21
use Kunstmaan\SearchBundle\Provider\SearchProviderInterface;
22
use Kunstmaan\SearchBundle\Search\AnalysisFactoryInterface;
23
use Kunstmaan\UtilitiesBundle\Helper\ClassLookup;
24
use Psr\Log\LoggerInterface;
25
use Symfony\Component\DependencyInjection\ContainerInterface;
26
use Symfony\Component\DomCrawler\Crawler;
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');
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('KunstmaanNodeBundle:Node');
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);
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));
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();
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('KunstmaanNodeBundle:Node')->getRootNodeFor(
0 ignored issues
show
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,
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
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
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
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
        $renderer = $this->container->get('templating');
563
        $doc['content'] = '';
564
565
        if ($page instanceof SearchViewTemplateInterface) {
566
            $doc['content'] = $this->renderCustomSearchView($nodeTranslation, $page, $renderer);
567
568
            return null;
569
        }
570
571
        if ($page instanceof HasPagePartsInterface) {
572
            $doc['content'] = $this->renderDefaultSearchView($nodeTranslation, $page, $renderer);
573
574
            return null;
575
        }
576
    }
577
578
    /**
579
     * Enter request scope if it is not active yet...
580
     *
581
     * @param string $lang
582
     */
583
    protected function enterRequestScope($lang)
584
    {
585
        $locale = null;
586
        $requestStack = $this->container->get('request_stack');
587
        // If there already is a request, get the locale from it.
588
        if ($requestStack->getCurrentRequest()) {
589
            $locale = $requestStack->getCurrentRequest()->getLocale();
590
        }
591
        // If we don't have a request or the current request locale is different from the node langauge
592
        if (!$requestStack->getCurrentRequest() || ($locale && $locale !== $lang)) {
593
            $request = new Request();
594
            $request->setLocale($lang);
595
596
            $context = $this->container->get('router')->getContext();
597
            $context->setParameter('_locale', $lang);
598
599
            $requestStack->push($request);
600
        }
601
    }
602
603
    /**
604
     * Render a custom search view
605
     *
606
     * @param NodeTranslation             $nodeTranslation
607
     * @param SearchViewTemplateInterface $page
608
     * @param EngineInterface             $renderer
609
     *
610
     * @return string
611
     */
612
    protected function renderCustomSearchView(
613
        NodeTranslation $nodeTranslation,
614
        SearchViewTemplateInterface $page,
615
        EngineInterface $renderer
616
    ) {
617
        $view = $page->getSearchView();
618
        $renderContext = new RenderContext([
619
            'locale' => $nodeTranslation->getLang(),
620
            'page' => $page,
621
            'indexMode' => true,
622
            'nodetranslation' => $nodeTranslation,
623
        ]);
624
625
        if ($page instanceof PageInterface) {
626
            $request = $this->container->get('request_stack')->getCurrentRequest();
627
            $page->service($this->container, $request, $renderContext);
628
        }
629
630
        $content = $this->removeHtml(
631
            $renderer->render(
632
                $view,
633
                $renderContext->getArrayCopy()
634
            )
635
        );
636
637
        return $content;
638
    }
639
640
    /**
641
     * Render default search view (all indexable pageparts in the main context
642
     * of the page)
643
     *
644
     * @param NodeTranslation       $nodeTranslation
645
     * @param HasPagePartsInterface $page
646
     * @param EngineInterface       $renderer
647
     *
648
     * @return string
649
     */
650
    protected function renderDefaultSearchView(
651
        NodeTranslation $nodeTranslation,
652
        HasPagePartsInterface $page,
653
        EngineInterface $renderer
654
    ) {
655
        $pageparts = $this->indexablePagePartsService->getIndexablePageParts($page);
656
        $view = '@KunstmaanNodeSearch/PagePart/view.html.twig';
657
        $content = $this->removeHtml(
658
            $renderer->render(
659
                $view,
660
                array(
661
                    'locale' => $nodeTranslation->getLang(),
662
                    'page' => $page,
663
                    'pageparts' => $pageparts,
664
                    'indexMode' => true,
665
                )
666
            )
667
        );
668
669
        return $content;
670
    }
671
672
    /**
673
     * Add custom data to index document (you can override to add custom fields
674
     * to the search index)
675
     *
676
     * @param HasNodeInterface $page
677
     * @param array            $doc
678
     */
679
    protected function addCustomData(HasNodeInterface $page, &$doc)
680
    {
681
        $event = new IndexNodeEvent($page, $doc);
682
        $this->container->get('event_dispatcher')->dispatch(IndexNodeEvent::EVENT_INDEX_NODE, $event);
683
684
        $doc = $event->doc;
685
686
        if ($page instanceof HasCustomSearchDataInterface) {
687
            $doc += $page->getCustomSearchData($doc);
688
        }
689
    }
690
691
    /**
692
     * Convert a DateTime to UTC equivalent...
693
     *
694
     * @param \DateTime $dateTime
695
     *
696
     * @return \DateTime
697
     */
698
    protected function getUTCDateTime(\DateTime $dateTime)
699
    {
700
        $result = clone $dateTime;
701
        $result->setTimezone(new \DateTimeZone('UTC'));
702
703
        return $result;
704
    }
705
706
    /**
707
     * Removes all HTML markup & decode HTML entities
708
     *
709
     * @param $text
710
     *
711
     * @return string
712
     */
713
    protected function removeHtml($text)
714
    {
715
        if (!trim($text)) {
716
            return '';
717
        }
718
719
        // Remove Styles and Scripts
720
        $crawler = new Crawler();
721
        $crawler->addHtmlContent($text);
722
        $crawler->filter('style, script')->each(function (Crawler $crawler) {
723
            foreach ($crawler as $node) {
724
                $node->parentNode->removeChild($node);
725
            }
726
        });
727
        $text = $crawler->html();
728
729
        // Remove HTML markup
730
        $result = strip_tags($text);
731
732
        // Decode HTML entities
733
        $result = trim(html_entity_decode($result, ENT_QUOTES));
734
735
        return $result;
736
    }
737
738
    /**
739
     * Fetch ACL permissions for the specified entity
740
     *
741
     * @param object $object
742
     *
743
     * @return array
744
     */
745
    protected function getAclPermissions($object)
746
    {
747
        $roles = [];
748
749
        try {
750
            $objectIdentity = ObjectIdentity::fromDomainObject($object);
751
752
            /* @var AclInterface $acl */
753
            $acl = $this->aclProvider->findAcl($objectIdentity);
754
            $objectAces = $acl->getObjectAces();
755
756
            /* @var AuditableEntryInterface $ace */
757 View Code Duplication
            foreach ($objectAces as $ace) {
758
                $securityIdentity = $ace->getSecurityIdentity();
759
                if (
760
                    $securityIdentity instanceof RoleSecurityIdentity &&
761
                    ($ace->getMask() & MaskBuilder::MASK_VIEW != 0)
762
                ) {
763
                    $roles[] = $securityIdentity->getRole();
764
                }
765
            }
766
        } catch (AclNotFoundException $e) {
767
            // No ACL found... assume default
768
            $roles = array('IS_AUTHENTICATED_ANONYMOUSLY');
769
        }
770
771
        return $roles;
772
    }
773
774
    /**
775
     * @param $publicNodeVersion
776
     *
777
     * @return mixed
778
     */
779
    private function getNodeRefPage(NodeVersion $publicNodeVersion)
780
    {
781
        $refEntityName = $publicNodeVersion->getRefEntityName();
782
783
        if (!isset($this->nodeRefs[$refEntityName])) {
784
            $this->nodeRefs[$refEntityName] = new $refEntityName();
785
        }
786
787
        return $this->nodeRefs[$refEntityName];
788
    }
789
}
790