Passed
Push — master ( 7e6ca3...db41d3 )
by Timo
11:44 queued 08:35
created

Indexer   F

Complexity

Total Complexity 78

Size/Duplication

Total Lines 660
Duplicated Lines 0 %

Test Coverage

Coverage 91.32%

Importance

Changes 4
Bugs 1 Features 0
Metric Value
wmc 78
eloc 220
c 4
b 1
f 0
dl 0
loc 660
ccs 200
cts 219
cp 0.9132
rs 2.16

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A index() 0 25 3
A getSolrConnectionsByItem() 0 32 5
A preAddModifyDocuments() 0 21 4
A getBaseDocument() 0 6 1
A indexItem() 0 33 4
A getItemTypeConfiguration() 0 13 4
A getAdditionalDocuments() 0 26 6
A getFieldConfigurationFromItemRootPage() 0 8 2
A isRootPageIdPartOfRootLine() 0 14 2
A itemToDocument() 0 12 2
A getFieldConfigurationFromItemRecordPage() 0 7 2
A getAccessRootline() 0 17 3
B getFullItemRecord() 0 48 10
A processDocuments() 0 13 2
A getItemRecordOverlayed() 0 15 3
A getSystemLanguages() 0 3 1
A getConnectionsForIndexableLanguages() 0 18 3
A getDefaultLanguageUid() 0 12 5
A setLogging() 0 5 1
A log() 0 28 4
B getTranslationOverlaysWithConfiguredSite() 0 33 10

How to fix   Complexity   

Complex Class

Complex classes like Indexer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Indexer, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace ApacheSolrForTypo3\Solr\IndexQueue;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2009-2015 Ingo Renner <[email protected]>
8
 *  All rights reserved
9
 *
10
 *  This script is part of the TYPO3 project. The TYPO3 project is
11
 *  free software; you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 3 of the License, or
14
 *  (at your option) any later version.
15
 *
16
 *  The GNU General Public License can be found at
17
 *  http://www.gnu.org/copyleft/gpl.html.
18
 *
19
 *  This script is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  This copyright notice MUST APPEAR in all copies of the script!
25
 ***************************************************************/
26
27
use ApacheSolrForTypo3\Solr\ConnectionManager;
28
use ApacheSolrForTypo3\Solr\Domain\Search\ApacheSolrDocument\Builder;
29
use ApacheSolrForTypo3\Solr\FieldProcessor\Service;
30
use ApacheSolrForTypo3\Solr\NoSolrConnectionFoundException;
31
use ApacheSolrForTypo3\Solr\Domain\Site\Site;
32
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
33
use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository;
34
use ApacheSolrForTypo3\Solr\System\Solr\Document\Document;
35
use ApacheSolrForTypo3\Solr\System\Solr\ResponseAdapter;
36
use ApacheSolrForTypo3\Solr\System\Solr\SolrConnection;
37
use ApacheSolrForTypo3\Solr\Util;
38
use Solarium\Exception\HttpException;
39
use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider;
40
use TYPO3\CMS\Core\Utility\GeneralUtility;
41
use TYPO3\CMS\Core\Utility\RootlineUtility;
42
use TYPO3\CMS\Frontend\Page\PageRepository;
43
44
/**
45
 * A general purpose indexer to be used for indexing of any kind of regular
46
 * records like tt_news, tt_address, and so on.
47
 * Specialized indexers can extend this class to handle advanced stuff like
48
 * category resolution in tt_news or file indexing.
49
 *
50
 * @author Ingo Renner <[email protected]>
51
 */
52
class Indexer extends AbstractIndexer
53
{
54
55
    # TODO change to singular $document instead of plural $documents
56
57
    /**
58
     * A Solr service instance to interact with the Solr server
59
     *
60
     * @var SolrConnection
61
     */
62
    protected $solr;
63
64
    /**
65
     * @var ConnectionManager
66
     */
67
    protected $connectionManager;
68
69
    /**
70
     * Holds options for a specific indexer
71
     *
72
     * @var array
73
     */
74
    protected $options = [];
75
76
    /**
77
     * To log or not to log... #Shakespeare
78
     *
79
     * @var bool
80
     */
81
    protected $loggingEnabled = false;
82
83
    /**
84
     * @var SolrLogManager
85
     */
86
    protected $logger = null;
87
88
    /**
89
     * @var PagesRepository
90
     */
91
    protected $pagesRepository;
92
93
    /**
94
     * @var Builder
95
     */
96
    protected $documentBuilder;
97
98
    /**
99
     * Constructor
100
     *
101
     * @param array $options array of indexer options
102
     * @param PagesRepository|null $pagesRepository
103
     * @param Builder|null $documentBuilder
104
     * @param SolrLogManager|null $logger
105
     * @param ConnectionManager|null $connectionManager
106
     */
107 37
    public function __construct(array $options = [], PagesRepository $pagesRepository = null, Builder $documentBuilder = null, SolrLogManager $logger = null, ConnectionManager $connectionManager = null)
108
    {
109 37
        $this->options = $options;
110 37
        $this->pagesRepository = $pagesRepository ?? GeneralUtility::makeInstance(PagesRepository::class);
111 37
        $this->documentBuilder = $documentBuilder ?? GeneralUtility::makeInstance(Builder::class);
112 37
        $this->logger = $logger ?? GeneralUtility::makeInstance(SolrLogManager::class, /** @scrutinizer ignore-type */ __CLASS__);
113 37
        $this->connectionManager = $connectionManager ?? GeneralUtility::makeInstance(ConnectionManager::class);
114 37
    }
115
116
    /**
117
     * Indexes an item from the indexing queue.
118
     *
119
     * @param Item $item An index queue item
120
     * @return bool returns true when indexed, false when not
121
     */
122 21
    public function index(Item $item)
123
    {
124 21
        $indexed = true;
125
126 21
        $this->type = $item->getType();
127 21
        $this->setLogging($item);
128
129 21
        $solrConnections = $this->getSolrConnectionsByItem($item);
130 21
        foreach ($solrConnections as $systemLanguageUid => $solrConnection) {
131 21
            $this->solr = $solrConnection;
132
133 21
            if (!$this->indexItem($item, $systemLanguageUid)) {
134
                /*
135
                 * A single language voting for "not indexed" should make the whole
136
                 * item count as being not indexed, even if all other languages are
137
                 * indexed.
138
                 * If there is no translation for a single language, this item counts
139
                 * as TRUE since it's not an error which that should make the item
140
                 * being reindexed during another index run.
141
                 */
142
                $indexed = false;
143
            }
144
        }
145
146 21
        return $indexed;
147
    }
148
149
    /**
150
     * Creates a single Solr Document for an item in a specific language.
151
     *
152
     * @param Item $item An index queue item to index.
153
     * @param int $language The language to use.
154
     * @return bool TRUE if item was indexed successfully, FALSE on failure
155
     */
156 21
    protected function indexItem(Item $item, $language = 0)
157
    {
158 21
        $itemIndexed = false;
159 21
        $documents = [];
160
161 21
        $itemDocument = $this->itemToDocument($item, $language);
162 21
        if (is_null($itemDocument)) {
163
            /*
164
             * If there is no itemDocument, this means there was no translation
165
             * for this record. This should not stop the current item to count as
166
             * being valid because not-indexing not-translated items is perfectly
167
             * fine.
168
             */
169 1
            return true;
170
        }
171
172 21
        $documents[] = $itemDocument;
173 21
        $documents = array_merge($documents, $this->getAdditionalDocuments($item, $language, $itemDocument));
174 21
        $documents = $this->processDocuments($item, $documents);
175 21
        $documents = $this->preAddModifyDocuments($item, $language, $documents);
176
177
        try {
178 21
            $response = $this->solr->getWriteService()->addDocuments($documents);
179 21
            if ($response->getHttpStatus() == 200) {
180 21
                $itemIndexed = true;
181
            }
182
        } catch (HttpException $e) {
183
            $response = new ResponseAdapter($e->getBody(), $httpStatus = 500, $e->getStatusMessage());
184
        }
185
186 21
        $this->log($item, $documents, $response);
187
188 21
        return $itemIndexed;
189
    }
190
191
    /**
192
     * Gets the full item record.
193
     *
194
     * This general record indexer simply gets the record from the item. Other
195
     * more specialized indexers may provide more data for their specific item
196
     * types.
197
     *
198
     * @param Item $item The item to be indexed
199
     * @param int $language Language Id (sys_language.uid)
200
     * @return array|NULL The full record with fields of data to be used for indexing or NULL to prevent an item from being indexed
201
     */
202 21
    protected function getFullItemRecord(Item $item, $language = 0)
203
    {
204 21
        Util::initializeTsfe($item->getRootPageUid(), $language);
205
206 21
        $systemLanguageContentOverlay = $GLOBALS['TSFE']->sys_language_contentOL;
207 21
        $itemRecord = $this->getItemRecordOverlayed($item, $language, $systemLanguageContentOverlay);
208
209
        /*
210
         * Skip disabled records. This happens if the default language record
211
         * is hidden but a certain translation isn't. Then the default language
212
         * document appears here but must not be indexed.
213
         */
214 21
        if (!empty($GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['disabled'])
215 21
            && $itemRecord[$GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['disabled']]
216
        ) {
217
            $itemRecord = null;
218
        }
219
220
        /*
221
         * Skip translation mismatching records. Sometimes the requested language
222
         * doesn't fit the returned language. This might happen with content fallback
223
         * and is perfectly fine in general.
224
         * But if the requested language doesn't match the returned language and
225
         * the given record has no translation parent, the indexqueue_item most
226
         * probably pointed to a non-translated language record that is dedicated
227
         * to a very specific language. Now we have to avoid indexing this record
228
         * into all language cores.
229
         */
230 21
        $translationOriginalPointerField = 'l10n_parent';
231 21
        if (!empty($GLOBALS['TCA'][$item->getType()]['ctrl']['transOrigPointerField'])) {
232 21
            $translationOriginalPointerField = $GLOBALS['TCA'][$item->getType()]['ctrl']['transOrigPointerField'];
233
        }
234
235 21
        $languageField = $GLOBALS['TCA'][$item->getType()]['ctrl']['languageField'];
236 21
        if ($itemRecord[$translationOriginalPointerField] == 0
237 21
            && $systemLanguageContentOverlay != 1
238 21
            && !empty($languageField)
239 21
            && $itemRecord[$languageField] != $language
240 21
            && $itemRecord[$languageField] != '-1'
241
        ) {
242 1
            $itemRecord = null;
243
        }
244
245 21
        if (!is_null($itemRecord)) {
246 21
            $itemRecord['__solr_index_language'] = $language;
247
        }
248
249 21
        return $itemRecord;
250
    }
251
252
    /**
253
     * Returns the overlayed item record.
254
     *
255
     * @param Item $item
256
     * @param int $language
257
     * @param string|null $systemLanguageContentOverlay
258
     * @return array|mixed|null
259
     */
260 21
    protected function getItemRecordOverlayed(Item $item, $language, $systemLanguageContentOverlay)
261
    {
262 21
        $itemRecord = $item->getRecord();
263
264 21
        if ($language > 0) {
265 6
            $page = GeneralUtility::makeInstance(PageRepository::class);
266 6
            $page->init(false);
267 6
            $itemRecord = $page->getRecordOverlay($item->getType(), $itemRecord, $language, $systemLanguageContentOverlay);
268
        }
269
270 21
        if (!$itemRecord) {
271 1
            $itemRecord = null;
272
        }
273
274 21
        return $itemRecord;
275
    }
276
277
    /**
278
     * Gets the configuration how to process an item's fields for indexing.
279
     *
280
     * @param Item $item An index queue item
281
     * @param int $language Language ID
282
     * @throws \RuntimeException
283
     * @return array Configuration array from TypoScript
284
     */
285 21
    protected function getItemTypeConfiguration(Item $item, $language = 0)
286
    {
287 21
        $indexConfigurationName = $item->getIndexingConfigurationName();
288 21
        $fields = $this->getFieldConfigurationFromItemRecordPage($item, $language, $indexConfigurationName);
289 21
        if (!$this->isRootPageIdPartOfRootLine($item) || count($fields) === 0) {
290 2
            $fields = $this->getFieldConfigurationFromItemRootPage($item, $language, $indexConfigurationName);
291 2
            if (count($fields) === 0) {
292
                throw new \RuntimeException('The item indexing configuration "' . $item->getIndexingConfigurationName() .
293
                    '" on root page uid ' . $item->getRootPageUid() . ' could not be found!', 1455530112);
294
            }
295
        }
296
297 21
        return $fields;
298
    }
299
300
    /**
301
     * The method retrieves the field configuration of the items record page id (pid).
302
     *
303
     * @param Item $item
304
     * @param integer $language
305
     * @param string $indexConfigurationName
306
     * @return array
307
     */
308 21
    protected function getFieldConfigurationFromItemRecordPage(Item $item, $language, $indexConfigurationName)
309
    {
310
        try {
311 21
            $solrConfiguration = Util::getSolrConfigurationFromPageId($item->getRecordPageId(), true, $language);
312 20
            return $solrConfiguration->getIndexQueueFieldsConfigurationByConfigurationName($indexConfigurationName, []);
313 1
        } catch (\Exception $e) {
314 1
            return [];
315
        }
316
    }
317
318
    /**
319
     * The method returns the field configuration of the items root page id (uid of the related root page).
320
     *
321
     * @param Item $item
322
     * @param integer $language
323
     * @param string $indexConfigurationName
324
     * @return array
325
     */
326 2
    protected function getFieldConfigurationFromItemRootPage(Item $item, $language, $indexConfigurationName)
327
    {
328 2
        $solrConfiguration = Util::getSolrConfigurationFromPageId($item->getRootPageUid(), true, $language);
329 2
        if (empty($solrConfiguration->getIndexQueueAdditionalPageIdsByConfigurationName($indexConfigurationName))) {
330
            return [];
331
        }
332
333 2
        return $solrConfiguration->getIndexQueueFieldsConfigurationByConfigurationName($indexConfigurationName, []);
334
    }
335
336
    /**
337
     * In case of additionalStoragePid config recordPageId can be outsite of siteroot.
338
     * In that case we should not read TS config of foreign siteroot.
339
     *
340
     * @return bool
341
     */
342 21
    protected function isRootPageIdPartOfRootLine(Item $item)
343
    {
344 21
        $rootPageId = $item->getRootPageUid();
345 21
        $buildRootlineWithPid = $item->getRecordPageId();
346 21
        if ($item->getType() === 'pages') {
347 1
            $buildRootlineWithPid = $item->getRecordUid();
348
        }
349 21
        $rootlineUtility = GeneralUtility::makeInstance(RootlineUtility::class, $buildRootlineWithPid);
350 21
        $rootline = $rootlineUtility->get();
351
352 21
        $pageInRootline = array_filter($rootline, function($page) use ($rootPageId) {
353 21
            return (int)$page['uid'] === $rootPageId;
354 21
        });
355 21
        return !empty($pageInRootline);
356
    }
357
358
    /**
359
     * Converts an item array (record) to a Solr document by mapping the
360
     * record's fields onto Solr document fields as configured in TypoScript.
361
     *
362
     * @param Item $item An index queue item
363
     * @param int $language Language Id
364
     * @return Document The Solr document converted from the record
365
     */
366 21
    protected function itemToDocument(Item $item, $language = 0)
367
    {
368 21
        $document = null;
369
370 21
        $itemRecord = $this->getFullItemRecord($item, $language);
371 21
        if (!is_null($itemRecord)) {
372 21
            $itemIndexingConfiguration = $this->getItemTypeConfiguration($item, $language);
373 21
            $document = $this->getBaseDocument($item, $itemRecord);
374 21
            $document = $this->addDocumentFieldsFromTyposcript($document, $itemIndexingConfiguration, $itemRecord);
375
        }
376
377 21
        return $document;
378
    }
379
380
    /**
381
     * Creates a Solr document with the basic / core fields set already.
382
     *
383
     * @param Item $item The item to index
384
     * @param array $itemRecord The record to use to build the base document
385
     * @return Document A basic Solr document
386
     */
387 21
    protected function getBaseDocument(Item $item, array $itemRecord)
388
    {
389 21
        $type = $item->getType();
390 21
        $rootPageUid = $item->getRootPageUid();
391 21
        $accessRootLine = $this->getAccessRootline($item);
392 21
        return $this->documentBuilder->fromRecord($itemRecord, $type, $rootPageUid, $accessRootLine);
393
    }
394
395
    /**
396
     * Generates an Access Rootline for an item.
397
     *
398
     * @param Item $item Index Queue item to index.
399
     * @return string The Access Rootline for the item
400
     */
401 21
    protected function getAccessRootline(Item $item)
402
    {
403 21
        $accessRestriction = '0';
404 21
        $itemRecord = $item->getRecord();
405
406
        // TODO support access restrictions set on storage page
407
408 21
        if (isset($GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['fe_group'])) {
409 1
            $accessRestriction = $itemRecord[$GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['fe_group']];
410
411 1
            if (empty($accessRestriction)) {
412
                // public
413 1
                $accessRestriction = '0';
414
            }
415
        }
416
417 21
        return 'r:' . $accessRestriction;
418
    }
419
420
    /**
421
     * Sends the documents to the field processing service which takes care of
422
     * manipulating fields as defined in the field's configuration.
423
     *
424
     * @param Item $item An index queue item
425
     * @param array $documents An array of Apache_Solr_Document objects to manipulate.
426
     * @return Document[] array Array of manipulated Document objects.
427
     */
428 21
    protected function processDocuments(Item $item, array $documents)
429
    {
430
        // needs to respect the TS settings for the page the item is on, conditions may apply
431 21
        $solrConfiguration = Util::getSolrConfigurationFromPageId($item->getRootPageUid());
432 21
        $fieldProcessingInstructions = $solrConfiguration->getIndexFieldProcessingInstructionsConfiguration();
433
434
        // same as in the FE indexer
435 21
        if (is_array($fieldProcessingInstructions)) {
0 ignored issues
show
introduced by
The condition is_array($fieldProcessingInstructions) is always true.
Loading history...
436 21
            $service = GeneralUtility::makeInstance(Service::class);
437 21
            $service->processDocuments($documents, $fieldProcessingInstructions);
438
        }
439
440 21
        return $documents;
441
    }
442
443
    /**
444
     * Allows third party extensions to provide additional documents which
445
     * should be indexed for the current item.
446
     *
447
     * @param Item $item The item currently being indexed.
448
     * @param int $language The language uid currently being indexed.
449
     * @param Document $itemDocument The document representing the item for the given language.
450
     * @return Document[] array An array of additional Document objects to index.
451
     */
452 25
    protected function getAdditionalDocuments(Item $item, $language, Document $itemDocument)
453
    {
454 25
        $documents = [];
455
456 25
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['indexItemAddDocuments'])) {
457 4
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['indexItemAddDocuments'] as $classReference) {
458 4
                if (!class_exists($classReference)) {
459 2
                    throw new \InvalidArgumentException('Class does not exits' . $classReference, 1490363487);
460
                }
461 2
                $additionalIndexer = GeneralUtility::makeInstance($classReference);
462 2
                if ($additionalIndexer instanceof AdditionalIndexQueueItemIndexer) {
463 1
                    $additionalDocuments = $additionalIndexer->getAdditionalItemDocuments($item, $language, $itemDocument);
464
465 1
                    if (is_array($additionalDocuments)) {
466 1
                        $documents = array_merge($documents,
467 1
                            $additionalDocuments);
468
                    }
469
                } else {
470 1
                    throw new \UnexpectedValueException(
471 1
                        get_class($additionalIndexer) . ' must implement interface ' . AdditionalIndexQueueItemIndexer::class,
472 1
                        1326284551
473
                    );
474
                }
475
            }
476
        }
477 22
        return $documents;
478
    }
479
480
    /**
481
     * Provides a hook to manipulate documents right before they get added to
482
     * the Solr index.
483
     *
484
     * @param Item $item The item currently being indexed.
485
     * @param int $language The language uid of the documents
486
     * @param array $documents An array of documents to be indexed
487
     * @return array An array of modified documents
488
     */
489 21
    protected function preAddModifyDocuments(Item $item, $language, array $documents)
490
    {
491 21
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['preAddModifyDocuments'])) {
492
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['preAddModifyDocuments'] as $classReference) {
493
                $documentsModifier = GeneralUtility::makeInstance($classReference);
494
495
                if ($documentsModifier instanceof PageIndexerDocumentsModifier) {
496
                    $documents = $documentsModifier->modifyDocuments($item, $language, $documents);
497
                } else {
498
                    throw new \RuntimeException(
499
                        'The class "' . get_class($documentsModifier)
500
                        . '" registered as document modifier in hook
501
							preAddModifyDocuments must implement interface
502
							ApacheSolrForTypo3\Solr\IndexQueue\PageIndexerDocumentsModifier',
503
                        1309522677
504
                    );
505
                }
506
            }
507
        }
508
509 21
        return $documents;
510
    }
511
512
    // Initialization
513
514
    /**
515
     * Gets the Solr connections applicable for an item.
516
     *
517
     * The connections include the default connection and connections to be used
518
     * for translations of an item.
519
     *
520
     * @param Item $item An index queue item
521
     * @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
522
     */
523 23
    protected function getSolrConnectionsByItem(Item $item)
524
    {
525 23
        $solrConnections = [];
526
527 23
        $pageId = $item->getRootPageUid();
528 23
        if ($item->getType() === 'pages') {
529 2
            $pageId = $item->getRecordUid();
530
        }
531
532
        // Solr configurations possible for this item
533 23
        $site = $item->getSite();
534
535 23
        $solrConfigurationsBySite = $this->connectionManager->getConfigurationsBySite($site);
0 ignored issues
show
Deprecated Code introduced by
The function ApacheSolrForTypo3\Solr\...tConfigurationsBySite() has been deprecated: will be removed in v11, use $site->getAllSolrConnectionConfigurations() ( Ignorable by Annotation )

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

535
        $solrConfigurationsBySite = /** @scrutinizer ignore-deprecated */ $this->connectionManager->getConfigurationsBySite($site);

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

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

Loading history...
536 23
        $siteLanguages = [];
537 23
        foreach ($solrConfigurationsBySite as $solrConfiguration) {
538 23
            $siteLanguages[] = $solrConfiguration['language'];
539
        }
540
541 23
        $defaultLanguageUid = $this->getDefaultLanguageUid($item, $site->getRootPage(), $siteLanguages);
542 23
        $translationOverlays = $this->getTranslationOverlaysWithConfiguredSite((int)$pageId, $site, (array)$siteLanguages);
543
544 23
        $defaultConnection = $this->connectionManager->getConnectionByPageId($pageId, 0, $item->getMountPointIdentifier());
545 23
        $translationConnections = $this->getConnectionsForIndexableLanguages($translationOverlays);
546
547 23
        if ($defaultLanguageUid == 0) {
548 22
            $solrConnections[0] = $defaultConnection;
549
        }
550
551 23
        foreach ($translationConnections as $systemLanguageUid => $solrConnection) {
552 7
            $solrConnections[$systemLanguageUid] = $solrConnection;
553
        }
554 23
        return $solrConnections;
555
    }
556
557
    /**
558
     * @param int $pageId
559
     * @param Site $site
560
     * @param array $siteLanguages
561
     * @return array
562
     */
563
    protected function getTranslationOverlaysWithConfiguredSite(int $pageId, Site $site, array $siteLanguages): array
564
    {
565
        $translationOverlays = $this->pagesRepository->findTranslationOverlaysByPageId($pageId);
566 23
        $translatedLanguages = [];
567
        foreach ($translationOverlays as $key => $translationOverlay) {
568 23
            if (!in_array($translationOverlay['sys_language_uid'], $siteLanguages)) {
569
                unset($translationOverlays[$key]);
570 23
            } else {
571 7
                $translatedLanguages[] = (int)$translationOverlay['sys_language_uid'];
572
            }
573
        }
574
575
        if (count($translationOverlays) + 1 !== count($siteLanguages)) {
576 23
            // not all Languages are translated
577
            // add Language Fallback
578
            foreach ($siteLanguages as $languageId) {
579
                if ($languageId !== 0 && !in_array((int)$languageId, $translatedLanguages, true)) {
580
                    $fallbackLanguageIds = $site->getFallbackOrder((int)$languageId);
581
                    foreach ($fallbackLanguageIds as $fallbackLanguageId) {
582
                        if ($fallbackLanguageId === 0 || in_array((int)$fallbackLanguageId, $translatedLanguages, true)) {
583
                            $translationOverlay = [
584
                                'pid' => $pageId,
585
                                'sys_language_uid' => $languageId,
586
                                'l10n_parent' => $pageId
587 23
                            ];
588
                            $translationOverlays[] = $translationOverlay;
589 23
                            continue 2;
590 23
                        }
591 1
                    }
592 1
                }
593 22
            }
594
        }
595
        return $translationOverlays;
596
    }
597
598 23
599
    /**
600
     * @param Item $item An index queue item
601
     * @param array $rootPage
602
     * @param array $siteLanguages
603
     *
604
     * @return int
605
     * @throws \RuntimeException
606
     */
607
    private function getDefaultLanguageUid(Item $item, array $rootPage, array $siteLanguages)
608
    {
609
        $defaultLanguageUid = 0;
610
        if (($rootPage['l18n_cfg'] & 1) == 1 && count($siteLanguages) > 1) {
611
            unset($siteLanguages[array_search('0', $siteLanguages)]);
612
            $defaultLanguageUid = $siteLanguages[min(array_keys($siteLanguages))];
613
        } elseif (($rootPage['l18n_cfg'] & 1) == 1 && count($siteLanguages) == 1) {
614
            $message = 'Root page ' . (int)$item->getRootPageUid() . ' is set to hide default translation, but no other language is configured!';
615 23
            throw new \RuntimeException($message);
616
        }
617 23
618 23
        return $defaultLanguageUid;
619
    }
620 23
621 23
    /**
622 23
     * Returns an array of system languages.
623 23
     *
624
     * @return array
625 23
     */
626 5
    protected function getSystemLanguages()
627
    {
628
        return GeneralUtility::makeInstance(TranslationConfigurationProvider::class)->getSystemLanguages();
629 18
    }
630 18
631 17
    /**
632 17
     * Checks for which languages connections have been configured and returns
633
     * these connections.
634 2
     *
635 2
     * @param array $translationOverlays An array of translation overlays to check for configured connections.
636 2
     * @return array An array of ApacheSolrForTypo3\Solr\System\Solr\SolrConnection connections.
637 2
     */
638
    protected function getConnectionsForIndexableLanguages(array $translationOverlays)
639
    {
640
        $connections = [];
641
642 23
        foreach ($translationOverlays as $translationOverlay) {
643
            $pageId = $translationOverlay['l10n_parent'];
644
            $languageId = $translationOverlay['sys_language_uid'];
645
646
            try {
647
                $connection = $this->connectionManager->getConnectionByPageId($pageId, $languageId);
648
                $connections[$languageId] = $connection;
649
            } catch (NoSolrConnectionFoundException $e) {
650 17
                // ignore the exception as we seek only those connections
651
                // actually available
652 17
            }
653
        }
654
655
        return $connections;
656
    }
657
658
    // Utility methods
659
660
    // FIXME extract log() and setLogging() to ApacheSolrForTypo3\Solr\IndexQueue\AbstractIndexer
661
    // FIXME extract an interface Tx_Solr_IndexQueue_ItemInterface
662 23
663
    /**
664 23
     * Enables logging dependent on the configuration of the item's site
665
     *
666 23
     * @param Item $item An item being indexed
667 7
     * @return    void
668 7
     */
669
    protected function setLogging(Item $item)
670
    {
671 7
        $solrConfiguration = Util::getSolrConfigurationFromPageId($item->getRootPageUid());
672 7
        $this->loggingEnabled = $solrConfiguration->getLoggingIndexingQueueOperationsByConfigurationNameWithFallBack(
673
            $item->getIndexingConfigurationName()
674
        );
675
    }
676
677
    /**
678
     * Logs the item and what document was created from it
679 23
     *
680
     * @param Item $item The item that is being indexed.
681
     * @param array $itemDocuments An array of Solr documents created from the item's data
682
     * @param ResponseAdapter $response The Solr response for the particular index document
683
     */
684
    protected function log(Item $item, array $itemDocuments, ResponseAdapter $response)
685
    {
686
        if (!$this->loggingEnabled) {
687
            return;
688
        }
689
690
        $message = 'Index Queue indexing ' . $item->getType() . ':' . $item->getRecordUid() . ' - ';
691
692
        // preparing data
693 22
        $documents = [];
694
        foreach ($itemDocuments as $document) {
695 22
            $documents[] = (array)$document;
696 22
        }
697 22
698
        $logData = ['item' => (array)$item, 'documents' => $documents, 'response' => (array)$response];
699 22
700
        if ($response->getHttpStatus() == 200) {
701
            $severity = SolrLogManager::NOTICE;
702
            $message .= 'Success';
703
        } else {
704
            $severity = SolrLogManager::ERROR;
705
            $message .= 'Failure';
706
707
            $logData['status'] = $response->getHttpStatus();
708 21
            $logData['status message'] = $response->getHttpStatusMessage();
709
        }
710 21
711 21
        $this->logger->log($severity, $message, $logData);
712
    }
713
}
714