Passed
Push — master ( 7d0241...87493b )
by Timo
04:16
created

Classes/IndexQueue/Indexer.php (1 issue)

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

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