Passed
Push — task/2976_TYPO3.11_compatibili... ( 4d1a77...3b9190 )
by Rafael
03:39
created

getTranslationOverlaysWithConfiguredSite()   B

Complexity

Conditions 10
Paths 6

Size

Total Lines 33
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 10.0125

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 20
dl 0
loc 33
ccs 19
cts 20
cp 0.95
rs 7.6666
c 1
b 1
f 0
cc 10
nc 6
nop 3
crap 10.0125

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace ApacheSolrForTypo3\Solr\IndexQueue;
3
4
/*
5
 * This file is part of the TYPO3 CMS project.
6
 *
7
 * It is free software; you can redistribute it and/or modify it under
8
 * the terms of the GNU General Public License, either version 2
9
 * of the License, or any later version.
10
 *
11
 * For the full copyright and license information, please read the
12
 * LICENSE.txt file that was distributed with this source code.
13
 *
14
 * The TYPO3 project - inspiring people to share!
15
 */
16
17
use ApacheSolrForTypo3\Solr\ConnectionManager;
18
use ApacheSolrForTypo3\Solr\Domain\Search\ApacheSolrDocument\Builder;
19
use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository;
20
use ApacheSolrForTypo3\Solr\FieldProcessor\Service;
21
use ApacheSolrForTypo3\Solr\FrontendEnvironment;
22
use ApacheSolrForTypo3\Solr\FrontendEnvironment\Tsfe;
23
use ApacheSolrForTypo3\Solr\NoSolrConnectionFoundException;
24
use ApacheSolrForTypo3\Solr\Domain\Site\Site;
25
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
26
use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository;
27
use ApacheSolrForTypo3\Solr\System\Solr\Document\Document;
28
use ApacheSolrForTypo3\Solr\System\Solr\ResponseAdapter;
29
use ApacheSolrForTypo3\Solr\System\Solr\SolrConnection;
30
use Doctrine\DBAL\Driver\Exception as DBALDriverException;
31
use Exception;
32
use RuntimeException;
33
use Solarium\Exception\HttpException;
34
use TYPO3\CMS\Core\Context\LanguageAspectFactory;
35
use TYPO3\CMS\Core\Error\Http\InternalServerErrorException;
36
use TYPO3\CMS\Core\Error\Http\ServiceUnavailableException;
37
use TYPO3\CMS\Core\Exception\SiteNotFoundException;
38
use TYPO3\CMS\Core\Site\SiteFinder;
39
use TYPO3\CMS\Core\Utility\GeneralUtility;
40
use TYPO3\CMS\Core\Utility\RootlineUtility;
41
42
/**
43
 * A general purpose indexer to be used for indexing of any kind of regular
44
 * records like tt_news, tt_address, and so on.
45
 * Specialized indexers can extend this class to handle advanced stuff like
46
 * category resolution in tt_news or file indexing.
47
 *
48
 * @author Ingo Renner <[email protected]>
49
 * @copyright  (c) 2009-2015 Ingo Renner <[email protected]>
50
 */
51
class Indexer extends AbstractIndexer
52
{
53
    /**
54
     * A Solr service instance to interact with the Solr server
55
     *
56
     * @var SolrConnection
57
     */
58
    protected $solr;
59
60
    /**
61
     * @var ConnectionManager
62
     */
63
    protected $connectionManager;
64
65
    /**
66
     * Holds options for a specific indexer
67
     *
68
     * @var array
69
     */
70
    protected $options = [];
71
72
    /**
73
     * To log or not to log... #Shakespeare
74
     *
75
     * @var bool
76
     */
77
    protected $loggingEnabled = false;
78
79
    /**
80
     * @var SolrLogManager
81
     */
82
    protected $logger = null;
83
84
    /**
85
     * @var PagesRepository
86
     */
87
    protected $pagesRepository;
88
89
    /**
90
     * @var Builder
91
     */
92
    protected $documentBuilder;
93
94
    /**
95
     * @var FrontendEnvironment
96
     */
97
    protected $frontendEnvironment = null;
98
99
    /**
100
     * Constructor
101
     *
102
     * @param array $options array of indexer options
103
     * @param PagesRepository|null $pagesRepository
104
     * @param Builder|null $documentBuilder
105
     * @param SolrLogManager|null $logger
106
     * @param ConnectionManager|null $connectionManager
107
     * @param FrontendEnvironment|null $frontendEnvironment
108
     */
109 38
    public function __construct(
110
        array $options = [],
111
        PagesRepository $pagesRepository = null,
112
        Builder $documentBuilder = null,
113
        SolrLogManager $logger = null,
114
        ConnectionManager $connectionManager = null,
115
        FrontendEnvironment $frontendEnvironment = null
116
    )
117
    {
118 38
        $this->options = $options;
119 38
        $this->pagesRepository = $pagesRepository ?? GeneralUtility::makeInstance(PagesRepository::class);
120 38
        $this->documentBuilder = $documentBuilder ?? GeneralUtility::makeInstance(Builder::class);
121 38
        $this->logger = $logger ?? GeneralUtility::makeInstance(SolrLogManager::class, /** @scrutinizer ignore-type */ __CLASS__);
122 38
        $this->connectionManager = $connectionManager ?? GeneralUtility::makeInstance(ConnectionManager::class);
123 38
        $this->frontendEnvironment = $frontendEnvironment ?? GeneralUtility::makeInstance(FrontendEnvironment::class);
124 38
    }
125
126
    /**
127
     * Indexes an item from the indexing queue.
128
     *
129
     * @param Item $item An index queue item
130
     * @return bool returns true when indexed, false when not
131
     */
132 19
    public function index(Item $item)
133
    {
134 19
        $indexed = true;
135
136 19
        $this->type = $item->getType();
137 19
        $this->setLogging($item);
138
139 19
        $solrConnections = $this->getSolrConnectionsByItem($item);
140 19
        foreach ($solrConnections as $systemLanguageUid => $solrConnection) {
141 19
            $this->solr = $solrConnection;
142
143 19
            if (!$this->indexItem($item, (int)$systemLanguageUid)) {
144
                /*
145
                 * A single language voting for "not indexed" should make the whole
146
                 * item count as being not indexed, even if all other languages are
147
                 * indexed.
148
                 * If there is no translation for a single language, this item counts
149
                 * as TRUE since it's not an error which that should make the item
150
                 * being reindexed during another index run.
151
                 */
152
                $indexed = false;
153
            }
154
        }
155
156 19
        return $indexed;
157
    }
158
159
    /**
160
     * Creates a single Solr Document for an item in a specific language.
161
     *
162
     * @param Item $item An index queue item to index.
163
     * @param int $language The language to use.
164
     * @return bool TRUE if item was indexed successfully, FALSE on failure
165
     * @throws InternalServerErrorException
166
     * @throws ServiceUnavailableException
167
     * @throws SiteNotFoundException
168
     */
169 19
    protected function indexItem(Item $item, int $language = 0)
170
    {
171 19
        $itemIndexed = false;
172 19
        $documents = [];
173
174
175 19
        $itemDocument = $this->itemToDocument($item, $language);
176 19
        if (is_null($itemDocument)) {
177
            /*
178
             * If there is no itemDocument, this means there was no translation
179
             * for this record. This should not stop the current item to count as
180
             * being valid because not-indexing not-translated items is perfectly
181
             * fine.
182
             */
183
            return true;
184
        }
185
186 19
        $documents[] = $itemDocument;
187 19
        $documents = array_merge($documents, $this->getAdditionalDocuments($item, $language, $itemDocument));
188 19
        $documents = $this->processDocuments($item, $documents);
189 19
        $documents = $this->preAddModifyDocuments($item, $language, $documents);
190
191
        try {
192 19
            $response = $this->solr->getWriteService()->addDocuments($documents);
193 19
            if ($response->getHttpStatus() == 200) {
194 19
                $itemIndexed = true;
195
            }
196
        } catch (HttpException $e) {
197
            $response = new ResponseAdapter($e->getBody(), $httpStatus = 500, $e->getStatusMessage());
198
        }
199
200 19
        $this->log($item, $documents, $response);
201
202 19
        return $itemIndexed;
203
    }
204
205
    /**
206
     * Gets the full item record.
207
     *
208
     * This general record indexer simply gets the record from the item. Other
209
     * more specialized indexers may provide more data for their specific item
210
     * types.
211
     *
212
     * @param Item $item The item to be indexed
213
     * @param int $language Language Id (sys_language.uid)
214
     * @return array|NULL The full record with fields of data to be used for indexing or NULL to prevent an item from being indexed
215
     * @throws InternalServerErrorException
216
     * @throws ServiceUnavailableException
217
     * @throws SiteNotFoundException
218
     */
219 19
    protected function getFullItemRecord(Item $item, int $language = 0): ?array
220
    {
221 19
        $itemRecord = $this->getItemRecordOverlayed($item, $language);
222
223 19
        if (!is_null($itemRecord)) {
224 19
            $itemRecord['__solr_index_language'] = $language;
225
        }
226
227 19
        return $itemRecord;
228
    }
229
230
    /**
231
     * Returns the overlaid item record.
232
     *
233
     * @param Item $item
234
     * @param int $language
235
     * @return array|mixed|null
236
     * @throws DBALDriverException
237
     * @throws FrontendEnvironment\Exception\Exception
238
     * @throws SiteNotFoundException
239
     */
240 19
    protected function getItemRecordOverlayed(Item $item, int $language): ?array
241
    {
242 19
        $itemRecord = $item->getRecord();
243 19
        $languageField = $GLOBALS['TCA'][$item->getType()]['ctrl']['languageField'] ?? null;
244
        // skip "free content mode"-record for other languages, if item is a "free content mode"-record
245 19
        if ($this->isAFreeContentModeItemRecord($item, $language)
246 19
            && isset($languageField)
247 19
            && (int)($itemRecord[$languageField] ?? null) !== $language
248
        ) {
249
            return null;
250
        }
251
        // skip fallback for "free content mode"-languages
252 19
        if ($this->isLanguageInAFreeContentMode($item, $language)
253 19
            && isset($languageField)
254 19
            && (int)($itemRecord[$languageField] ?? null) !== $language
255
        ) {
256
            return null;
257
        }
258
259 19
        $pidToUse = $this->getPageIdOfItem($item);
260
261 19
        return GeneralUtility::makeInstance(Tsfe::class)
262 19
            ->getTsfeByPageIdAndLanguageId($pidToUse, $language, $item->getRootPageUid())
263 19
            ->sys_page->getLanguageOverlay($item->getType(), $itemRecord);
264
    }
265
266
    /**
267
     * @param Item $item
268
     * @param int $language
269
     *
270
     * @return bool
271
     */
272 19
    protected function isAFreeContentModeItemRecord(Item $item, int $language): bool
0 ignored issues
show
Unused Code introduced by
The parameter $language is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

272
    protected function isAFreeContentModeItemRecord(Item $item, /** @scrutinizer ignore-unused */ int $language): bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
273
    {
274 19
        $languageField = $GLOBALS['TCA'][$item->getType()]['ctrl']['languageField'] ?? null;
275 19
        $itemRecord = $item->getRecord();
276 19
        if (isset($languageField)
277 19
            && (int)($itemRecord[$languageField] ?? null) > 0
278 19
            && $this->isLanguageInAFreeContentMode($item, (int)($itemRecord[$languageField] ?? null))
279
        ) {
280
            return true;
281
        }
282 19
        return false;
283
    }
284
285
    /**
286
     * Gets the configuration how to process an item's fields for indexing.
287
     *
288
     * @param Item $item An index queue item
289
     * @param int $language Language ID
290
     * @throws RuntimeException
291
     * @return array Configuration array from TypoScript
292
     */
293 19
    protected function getItemTypeConfiguration(Item $item, int $language = 0): array
294
    {
295 19
        $indexConfigurationName = $item->getIndexingConfigurationName();
296 19
        $fields = $this->getFieldConfigurationFromItemRecordPage($item, $language, $indexConfigurationName);
297 19
        if (!$this->isRootPageIdPartOfRootLine($item) || count($fields) === 0) {
298 2
            $fields = $this->getFieldConfigurationFromItemRootPage($item, $language, $indexConfigurationName);
299 2
            if (count($fields) === 0) {
300
                throw new RuntimeException('The item indexing configuration "' . $item->getIndexingConfigurationName() .
301
                    '" on root page uid ' . $item->getRootPageUid() . ' could not be found!', 1455530112);
302
            }
303
        }
304
305 19
        return $fields;
306
    }
307
308
    /**
309
     * The method retrieves the field configuration of the items record page id (pid).
310
     *
311
     * @param Item $item
312
     * @param integer $language
313
     * @param string $indexConfigurationName
314
     * @return array
315
     */
316 19
    protected function getFieldConfigurationFromItemRecordPage(Item $item, int $language, string $indexConfigurationName): array
317
    {
318
        try {
319 19
            $pageId = $this->getPageIdOfItem($item);
320 19
            $solrConfiguration = $this->frontendEnvironment->getSolrConfigurationFromPageId($pageId, $language, $item->getRootPageUid());
321 19
            return $solrConfiguration->getIndexQueueFieldsConfigurationByConfigurationName($indexConfigurationName, []);
322
        } catch (\Throwable $e) {
323
            return [];
324
        }
325
    }
326
327
    /**
328
     * @param Item $item
329
     * @return int
330
     */
331 19
    protected function getPageIdOfItem(Item $item): int
332
    {
333 19
        if ($item->getType() === 'pages') {
334 2
            return $item->getRecordUid();
335
        }
336 17
        return $item->getRecordPageId();
337
    }
338
339
    /**
340
     * The method returns the field configuration of the items root page id (uid of the related root page).
341
     *
342
     * @param Item $item
343
     * @param integer $language
344
     * @param string $indexConfigurationName
345
     * @return array
346
     */
347 2
    protected function getFieldConfigurationFromItemRootPage(Item $item, int $language, string $indexConfigurationName): array
348
    {
349 2
        $solrConfiguration = $this->frontendEnvironment->getSolrConfigurationFromPageId($item->getRootPageUid(), $language);
350
351 2
        return $solrConfiguration->getIndexQueueFieldsConfigurationByConfigurationName($indexConfigurationName, []);
352
    }
353
354
    /**
355
     * In case of additionalStoragePid config recordPageId can be outside of siteroot.
356
     * In that case we should not read TS config of foreign siteroot.
357
     *
358
     * @param Item $item
359
     * @return bool
360
     */
361 19
    protected function isRootPageIdPartOfRootLine(Item $item): bool
362
    {
363 19
        $rootPageId = (int)$item->getRootPageUid();
364 19
        $buildRootlineWithPid = $this->getPageIdOfItem($item);
365 19
        $rootlineUtility = GeneralUtility::makeInstance(RootlineUtility::class, $buildRootlineWithPid);
366 19
        $rootline = $rootlineUtility->get();
367
368 19
        $pageInRootline = array_filter($rootline, function($page) use ($rootPageId) {
369 19
            return (int)$page['uid'] === $rootPageId;
370 19
        });
371 19
        return !empty($pageInRootline);
372
    }
373
374
    /**
375
     * Converts an item array (record) to a Solr document by mapping the
376
     * record's fields onto Solr document fields as configured in TypoScript.
377
     *
378
     * @param Item $item An index queue item
379
     * @param int $language Language Id
380
     *
381
     * @return Document|null The Solr document converted from the record
382
     *
383
     * @throws InternalServerErrorException
384
     * @throws ServiceUnavailableException
385
     * @throws SiteNotFoundException
386
     * @throws Exception
387
     */
388 19
    protected function itemToDocument(Item $item, int $language = 0): ?Document
389
    {
390 19
        $document = null;
391
392 19
        $itemRecord = $this->getFullItemRecord($item, $language);
393 19
        if (!is_null($itemRecord)) {
394 19
            $itemIndexingConfiguration = $this->getItemTypeConfiguration($item, $language);
395 19
            $document = $this->getBaseDocument($item, $itemRecord);
396 19
            $pidToUse = $this->getPageIdOfItem($item);
397 19
            $tsfe = GeneralUtility::makeInstance(Tsfe::class)->getTsfeByPageIdAndLanguageId($pidToUse, $language, $item->getRootPageUid());
398 19
            $document = $this->addDocumentFieldsFromTyposcript($document, $itemIndexingConfiguration, $itemRecord, $tsfe);
399
        }
400
401 19
        return $document;
402
    }
403
404
    /**
405
     * Creates a Solr document with the basic / core fields set already.
406
     *
407
     * @param Item $item The item to index
408
     * @param array $itemRecord The record to use to build the base document
409
     * @return Document A basic Solr document
410
     */
411 19
    protected function getBaseDocument(Item $item, array $itemRecord): Document
412
    {
413 19
        $type = $item->getType();
414 19
        $rootPageUid = $item->getRootPageUid();
415 19
        $accessRootLine = $this->getAccessRootline($item);
416 19
        return $this->documentBuilder->fromRecord($itemRecord, $type, $rootPageUid, $accessRootLine);
417
    }
418
419
    /**
420
     * Generates an Access Rootline for an item.
421
     *
422
     * @param Item $item Index Queue item to index.
423
     * @return string The Access Rootline for the item
424
     */
425 19
    protected function getAccessRootline(Item $item)
426
    {
427 19
        $accessRestriction = '0';
428 19
        $itemRecord = $item->getRecord();
429
430
        // TODO support access restrictions set on storage page
431
432 19
        if (isset($GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['fe_group'])) {
433 2
            $accessRestriction = $itemRecord[$GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['fe_group']];
434
435 2
            if (empty($accessRestriction)) {
436
                // public
437 2
                $accessRestriction = '0';
438
            }
439
        }
440
441 19
        return 'r:' . $accessRestriction;
442
    }
443
444
    /**
445
     * Sends the documents to the field processing service which takes care of
446
     * manipulating fields as defined in the field's configuration.
447
     *
448
     * @param Item $item An index queue item
449
     * @param array $documents An array of \ApacheSolrForTypo3\Solr\System\Solr\Document\Document objects to manipulate.
450
     * @return Document[] array Array of manipulated Document objects.
451
     */
452 19
    protected function processDocuments(Item $item, array $documents)
453
    {
454
        // needs to respect the TS settings for the page the item is on, conditions may apply
455 19
        $solrConfiguration = $this->frontendEnvironment->getSolrConfigurationFromPageId($item->getRootPageUid());
0 ignored issues
show
Unused Code introduced by
The assignment to $solrConfiguration is dead and can be removed.
Loading history...
456
457 19
        $siteRepository = GeneralUtility::makeInstance(SiteRepository::class);
458 19
        $solrConfiguration = $siteRepository->getSiteByPageId($item->getRootPageUid())->getSolrConfiguration();
459 19
        $fieldProcessingInstructions = $solrConfiguration->getIndexFieldProcessingInstructionsConfiguration();
460
461
        // same as in the FE indexer
462 19
        if (is_array($fieldProcessingInstructions)) {
463 19
            $service = GeneralUtility::makeInstance(Service::class);
464 19
            $service->processDocuments($documents, $fieldProcessingInstructions);
465
        }
466
467 19
        return $documents;
468
    }
469
470
    /**
471
     * Allows third party extensions to provide additional documents which
472
     * should be indexed for the current item.
473
     *
474
     * @param Item $item The item currently being indexed.
475
     * @param int $language The language uid currently being indexed.
476
     * @param Document $itemDocument The document representing the item for the given language.
477
     * @return Document[] array An array of additional Document objects to index.
478
     */
479 23
    protected function getAdditionalDocuments(Item $item, int $language, Document $itemDocument)
480
    {
481 23
        $documents = [];
482
483 23
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['indexItemAddDocuments'] ?? null)) {
484 4
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['indexItemAddDocuments'] as $classReference) {
485 4
                if (!class_exists($classReference)) {
486 2
                    throw new \InvalidArgumentException('Class does not exits' . $classReference, 1490363487);
487
                }
488 2
                $additionalIndexer = GeneralUtility::makeInstance($classReference);
489 2
                if ($additionalIndexer instanceof AdditionalIndexQueueItemIndexer) {
490 1
                    $additionalDocuments = $additionalIndexer->getAdditionalItemDocuments($item, $language, $itemDocument);
491
492 1
                    if (is_array($additionalDocuments)) {
493 1
                        $documents = array_merge($documents,
494
                            $additionalDocuments);
495
                    }
496
                } else {
497 1
                    throw new \UnexpectedValueException(
498 1
                        get_class($additionalIndexer) . ' must implement interface ' . AdditionalIndexQueueItemIndexer::class,
499 1
                        1326284551
500
                    );
501
                }
502
            }
503
        }
504 20
        return $documents;
505
    }
506
507
    /**
508
     * Provides a hook to manipulate documents right before they get added to
509
     * the Solr index.
510
     *
511
     * @param Item $item The item currently being indexed.
512
     * @param int $language The language uid of the documents
513
     * @param array $documents An array of documents to be indexed
514
     * @return array An array of modified documents
515
     */
516 19
    protected function preAddModifyDocuments(Item $item, int $language, array $documents)
517
    {
518 19
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['preAddModifyDocuments'] ?? null)) {
519
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['preAddModifyDocuments'] as $classReference) {
520
                $documentsModifier = GeneralUtility::makeInstance($classReference);
521
522
                if ($documentsModifier instanceof PageIndexerDocumentsModifier) {
523
                    $documents = $documentsModifier->modifyDocuments($item, $language, $documents);
524
                } else {
525
                    throw new RuntimeException(
526
                        'The class "' . get_class($documentsModifier)
527
                        . '" registered as document modifier in hook
528
							preAddModifyDocuments must implement interface
529
							ApacheSolrForTypo3\Solr\IndexQueue\PageIndexerDocumentsModifier',
530
                        1309522677
531
                    );
532
                }
533
            }
534
        }
535
536 19
        return $documents;
537
    }
538
539
    // Initialization
540
541
    /**
542
     * Gets the Solr connections applicable for an item.
543
     *
544
     * The connections include the default connection and connections to be used
545
     * for translations of an item.
546
     *
547
     * @param Item $item An index queue item
548
     * @return array An array of ApacheSolrForTypo3\Solr\System\Solr\SolrConnection connections, the array's keys are the sys_language_uid of the language of the connection
549
     * @throws NoSolrConnectionFoundException
550
     */
551 22
    protected function getSolrConnectionsByItem(Item $item): array
552
    {
553 22
        $solrConnections = [];
554
555 22
        $rootPageId = $item->getRootPageUid();
556 22
        if ($item->getType() === 'pages') {
557 4
            $pageId = $item->getRecordUid();
558
        } else {
559 18
            $pageId = $item->getRecordPageId();
560
        }
561
562
        // Solr configurations possible for this item
563 22
        $site = $item->getSite();
564 22
        $solrConfigurationsBySite = $site->getAllSolrConnectionConfigurations();
565 22
        $siteLanguages = [];
566 22
        foreach ($solrConfigurationsBySite as $solrConfiguration) {
567 22
            $siteLanguages[] = $solrConfiguration['language'];
568
        }
569
570 22
        $defaultLanguageUid = $this->getDefaultLanguageUid($item, $site->getRootPage(), $siteLanguages);
571 22
        $translationOverlays = $this->getTranslationOverlaysWithConfiguredSite((int)$pageId, $site, (array)$siteLanguages);
572
573 22
        $defaultConnection = $this->connectionManager->getConnectionByPageId($rootPageId, $defaultLanguageUid, $item->getMountPointIdentifier() ?? '');
574 22
        $translationConnections = $this->getConnectionsForIndexableLanguages($translationOverlays);
575
576 22
        if ($defaultLanguageUid == 0) {
577 20
            $solrConnections[0] = $defaultConnection;
578
        }
579
580 22
        foreach ($translationConnections as $systemLanguageUid => $solrConnection) {
581 19
            $solrConnections[$systemLanguageUid] = $solrConnection;
582
        }
583 22
        return $solrConnections;
584
    }
585
586
    /**
587
     * @param int $pageId
588
     * @param Site $site
589
     * @param array $siteLanguages
590
     * @return array
591
     */
592 22
    protected function getTranslationOverlaysWithConfiguredSite(int $pageId, Site $site, array $siteLanguages): array
593
    {
594 22
        $translationOverlays = $this->pagesRepository->findTranslationOverlaysByPageId($pageId);
595 22
        $translatedLanguages = [];
596 22
        foreach ($translationOverlays as $key => $translationOverlay) {
597 6
            if (!in_array($translationOverlay['sys_language_uid'], $siteLanguages)) {
598
                unset($translationOverlays[$key]);
599
            } else {
600 6
                $translatedLanguages[] = (int)$translationOverlay['sys_language_uid'];
601
            }
602
        }
603
604 22
        if (count($translationOverlays) + 1 !== count($siteLanguages)) {
605
            // not all Languages are translated
606
            // add Language Fallback
607 21
            foreach ($siteLanguages as $languageId) {
608 21
                if ($languageId !== 0 && !in_array((int)$languageId, $translatedLanguages, true)) {
609 21
                    $fallbackLanguageIds = $this->getFallbackOrder($site, (int)$languageId, (int)$pageId);
610 21
                    foreach ($fallbackLanguageIds as $fallbackLanguageId) {
611 20
                        if ($fallbackLanguageId === 0 || in_array((int)$fallbackLanguageId, $translatedLanguages, true)) {
612 14
                            $translationOverlay = [
613 14
                                'pid' => $pageId,
614 14
                                'sys_language_uid' => $languageId,
615 14
                                'l10n_parent' => $pageId
616
                            ];
617 14
                            $translationOverlays[] = $translationOverlay;
618 14
                            continue 2;
619
                        }
620
                    }
621
                }
622
            }
623
        }
624 22
        return $translationOverlays;
625
    }
626
627
    /**
628
     * @param Site $site
629
     * @param int $languageId
630
     * @param int $pageId
631
     * @return array
632
     */
633 21
    protected function getFallbackOrder(Site $site,  int $languageId, int $pageId): array
0 ignored issues
show
Unused Code introduced by
The parameter $pageId is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

633
    protected function getFallbackOrder(Site $site,  int $languageId, /** @scrutinizer ignore-unused */ int $pageId): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
634
    {
635 21
        $fallbackChain = [];
636 21
        $siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
637
        try {
638 21
            $site = $siteFinder->getSiteByRootPageId($site->getRootPageId());
639 20
            $languageAspect = LanguageAspectFactory::createFromSiteLanguage($site->getLanguageById($languageId));
640 20
            $fallbackChain = $languageAspect->getFallbackChain();
641 1
        } catch (SiteNotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
642
643
        }
644 21
        return $fallbackChain;
645
    }
646
647
    /**
648
     * @param Item $item An index queue item
649
     * @param array $rootPage
650
     * @param array $siteLanguages
651
     *
652
     * @return int
653
     * @throws RuntimeException
654
     */
655 22
    protected function getDefaultLanguageUid(Item $item, array $rootPage, array $siteLanguages)
656
    {
657 22
        $defaultLanguageUid = 0;
658 22
        if (($rootPage['l18n_cfg'] & 1) == 1 && count($siteLanguages) == 1 && $siteLanguages[min(array_keys($siteLanguages))] > 0) {
659
            $defaultLanguageUid = $siteLanguages[min(array_keys($siteLanguages))];
660 22
        } elseif (($rootPage['l18n_cfg'] & 1) == 1 && count($siteLanguages) > 1) {
661 2
            unset($siteLanguages[array_search('0', $siteLanguages)]);
662 2
            $defaultLanguageUid = $siteLanguages[min(array_keys($siteLanguages))];
663 20
        } elseif (($rootPage['l18n_cfg'] & 1) == 1 && count($siteLanguages) == 1) {
664
            $message = 'Root page ' . (int)$item->getRootPageUid() . ' is set to hide default translation, but no other language is configured!';
665
            throw new RuntimeException($message);
666
        }
667
668 22
        return $defaultLanguageUid;
669
    }
670
671
    /**
672
     * Checks for which languages connections have been configured and returns
673
     * these connections.
674
     *
675
     * @param array $translationOverlays An array of translation overlays to check for configured connections.
676
     * @return array An array of ApacheSolrForTypo3\Solr\System\Solr\SolrConnection connections.
677
     */
678 22
    protected function getConnectionsForIndexableLanguages(array $translationOverlays)
679
    {
680 22
        $connections = [];
681
682 22
        foreach ($translationOverlays as $translationOverlay) {
683 20
            $pageId = $translationOverlay['l10n_parent'];
684 20
            $languageId = $translationOverlay['sys_language_uid'];
685
686
            try {
687 20
                $connection = $this->connectionManager->getConnectionByPageId($pageId, $languageId);
688 19
                $connections[$languageId] = $connection;
689 1
            } catch (NoSolrConnectionFoundException $e) {
690
                // ignore the exception as we seek only those connections
691
                // actually available
692
            }
693
        }
694
695 22
        return $connections;
696
    }
697
698
    // Utility methods
699
700
    // FIXME extract log() and setLogging() to ApacheSolrForTypo3\Solr\IndexQueue\AbstractIndexer
701
    // FIXME extract an interface Tx_Solr_IndexQueue_ItemInterface
702
703
    /**
704
     * Enables logging dependent on the configuration of the item's site
705
     *
706
     * @param Item $item An item being indexed
707
     * @return    void
708
     */
709 20
    protected function setLogging(Item $item)
710
    {
711 20
        $solrConfiguration = $this->frontendEnvironment->getSolrConfigurationFromPageId($item->getRootPageUid());
712 20
        $this->loggingEnabled = $solrConfiguration->getLoggingIndexingQueueOperationsByConfigurationNameWithFallBack(
713 20
            $item->getIndexingConfigurationName()
714
        );
715 20
    }
716
717
    /**
718
     * Logs the item and what document was created from it
719
     *
720
     * @param Item $item The item that is being indexed.
721
     * @param array $itemDocuments An array of Solr documents created from the item's data
722
     * @param ResponseAdapter $response The Solr response for the particular index document
723
     */
724 19
    protected function log(Item $item, array $itemDocuments, ResponseAdapter $response)
725
    {
726 19
        if (!$this->loggingEnabled) {
727 19
            return;
728
        }
729
730
        $message = 'Index Queue indexing ' . $item->getType() . ':' . $item->getRecordUid() . ' - ';
731
732
        // preparing data
733
        $documents = [];
734
        foreach ($itemDocuments as $document) {
735
            $documents[] = (array)$document;
736
        }
737
738
        $logData = ['item' => (array)$item, 'documents' => $documents, 'response' => (array)$response];
739
740
        if ($response->getHttpStatus() == 200) {
741
            $severity = SolrLogManager::NOTICE;
742
            $message .= 'Success';
743
        } else {
744
            $severity = SolrLogManager::ERROR;
745
            $message .= 'Failure';
746
747
            $logData['status'] = $response->getHttpStatus();
748
            $logData['status message'] = $response->getHttpStatusMessage();
749
        }
750
751
        $this->logger->log($severity, $message, $logData);
752
    }
753
754
    /**
755
     * Returns the language field from given table or null
756
     *
757
     * @param string $tableName
758
     * @return string|null
759
     */
760
    protected function getLanguageFieldFromTable(string $tableName): ?string
761
    {
762
        $tableControl = $GLOBALS['TCA'][$tableName]['ctrl'] ?? [];
763
764
        if (!empty($tableControl['languageField'])) {
765
            return $tableControl['languageField'];
766
        }
767
768
        return null;
769
    }
770
771
    /**
772
     * Checks the given language, if it is in "free" mode.
773
     *
774
     * @param Item $item
775
     * @param int $language
776
     * @return bool
777
     */
778 19
    protected function isLanguageInAFreeContentMode(Item $item, int $language): bool
779
    {
780 19
        if ($language === 0) {
781 19
            return false;
782
        }
783 17
        $typo3site = $item->getSite()->getTypo3SiteObject();
784 17
        $typo3siteLanguage = $typo3site->getLanguageById($language);
785 17
        $typo3siteLanguageFallbackType = $typo3siteLanguage->getFallbackType();
786 17
        if ($typo3siteLanguageFallbackType === 'free') {
787
            return true;
788
        }
789 17
        return false;
790
    }
791
}
792