Passed
Push — master ( 0adc35...2485da )
by Timo
04:40
created

Indexer::isRootPageIdPartOfRootLine()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 14
ccs 8
cts 8
cp 1
rs 9.9666
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
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 36
     */
107
    public function __construct(array $options = [], PagesRepository $pagesRepository = null, Builder $documentBuilder = null, SolrLogManager $logger = null, ConnectionManager $connectionManager = null)
108 36
    {
109 36
        $this->options = $options;
110 36
        $this->pagesRepository = $pagesRepository ?? GeneralUtility::makeInstance(PagesRepository::class);
111 36
        $this->documentBuilder = $documentBuilder ?? GeneralUtility::makeInstance(Builder::class);
112 36
        $this->logger = $logger ?? GeneralUtility::makeInstance(SolrLogManager::class, /** @scrutinizer ignore-type */ __CLASS__);
113 36
        $this->connectionManager = $connectionManager ?? GeneralUtility::makeInstance(ConnectionManager::class);
114
    }
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 20
     */
122
    public function index(Item $item)
123 20
    {
124
        $indexed = true;
125 20
126 20
        $this->type = $item->getType();
127
        $this->setLogging($item);
128 20
129 20
        $solrConnections = $this->getSolrConnectionsByItem($item);
130 20
        foreach ($solrConnections as $systemLanguageUid => $solrConnection) {
131
            $this->solr = $solrConnection;
132 20
133
            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 20
                 */
142
                $indexed = false;
143
            }
144
        }
145 20
146
        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 20
     */
156
    protected function indexItem(Item $item, $language = 0)
157 20
    {
158 20
        $itemIndexed = false;
159
        $documents = [];
160 20
161 20
        $itemDocument = $this->itemToDocument($item, $language);
162
        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 1
             */
169
            return true;
170
        }
171 20
172 20
        $documents[] = $itemDocument;
173 20
        $documents = array_merge($documents, $this->getAdditionalDocuments($item, $language, $itemDocument));
174 20
        $documents = $this->processDocuments($item, $documents);
175
        $documents = $this->preAddModifyDocuments($item, $language, $documents);
176
177 20
        try {
178 20
            $response = $this->solr->getWriteService()->addDocuments($documents);
179 20
            if ($response->getHttpStatus() == 200) {
180
                $itemIndexed = true;
181
            }
182
        } catch (HttpException $e) {
183
            $response = new ResponseAdapter($e->getBody(), $httpStatus = 500, $e->getStatusMessage());
184
        }
185 20
186
        $this->log($item, $documents, $response);
187 20
188
        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 20
     */
202
    protected function getFullItemRecord(Item $item, $language = 0)
203 20
    {
204
        Util::initializeTsfe($item->getRootPageUid(), $language);
205 20
206 20
        $systemLanguageContentOverlay = $GLOBALS['TSFE']->sys_language_contentOL;
207
        $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 20
         */
214 20
        if (!empty($GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['disabled'])
215
            && $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 20
         */
230 20
        $translationOriginalPointerField = 'l10n_parent';
231 19
        if (!empty($GLOBALS['TCA'][$item->getType()]['ctrl']['transOrigPointerField'])) {
232
            $translationOriginalPointerField = $GLOBALS['TCA'][$item->getType()]['ctrl']['transOrigPointerField'];
233
        }
234 20
235 20
        $languageField = $GLOBALS['TCA'][$item->getType()]['ctrl']['languageField'];
236 20
        if ($itemRecord[$translationOriginalPointerField] == 0
237 20
            && $systemLanguageContentOverlay != 1
238 20
            && !empty($languageField)
239 20
            && $itemRecord[$languageField] != $language
240
            && $itemRecord[$languageField] != '-1'
241 1
        ) {
242
            $itemRecord = null;
243
        }
244 20
245 20
        if (!is_null($itemRecord)) {
246
            $itemRecord['__solr_index_language'] = $language;
247
        }
248 20
249
        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 20
     */
260
    protected function getItemRecordOverlayed(Item $item, $language, $systemLanguageContentOverlay)
261 20
    {
262
        $itemRecord = $item->getRecord();
263 20
264 6
        if ($language > 0) {
265 6
            $page = GeneralUtility::makeInstance(PageRepository::class);
266 6
            $page->init(false);
267
            $itemRecord = $page->getRecordOverlay($item->getType(), $itemRecord, $language, $systemLanguageContentOverlay);
268
        }
269 20
270
        if (!$itemRecord) {
271
            $itemRecord = null;
272
        }
273 20
274
        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 20
     */
285
    protected function getItemTypeConfiguration(Item $item, $language = 0)
286 20
    {
287 20
        $indexConfigurationName = $item->getIndexingConfigurationName();
288 20
        $fields = $this->getFieldConfigurationFromItemRecordPage($item, $language, $indexConfigurationName);
289 1
        if (!$this->isRootPageIdPartOfRootLine($item) || count($fields) === 0) {
290 1
            $fields = $this->getFieldConfigurationFromItemRootPage($item, $language, $indexConfigurationName);
291
            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 20
297
        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 20
     */
308
    protected function getFieldConfigurationFromItemRecordPage(Item $item, $language, $indexConfigurationName)
309
    {
310 20
        try {
311 19
            $solrConfiguration = Util::getSolrConfigurationFromPageId($item->getRecordPageId(), true, $language);
312 1
            return $solrConfiguration->getIndexQueueFieldsConfigurationByConfigurationName($indexConfigurationName, []);
313 1
        } catch (\Exception $e) {
314
            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 1
     */
326
    protected function getFieldConfigurationFromItemRootPage(Item $item, $language, $indexConfigurationName)
327 1
    {
328 1
        $solrConfiguration = Util::getSolrConfigurationFromPageId($item->getRootPageUid(), true, $language);
329
        if (empty($solrConfiguration->getIndexQueueAdditionalPageIdsByConfigurationName($indexConfigurationName))) {
330
            return [];
331
        }
332 1
333
        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
    protected function isRootPageIdPartOfRootLine(Item $item)
343 20
    {
344
        $rootPageId = $item->getRootPageUid();
345 20
        $buildRootlineWithPid = $item->getRecordPageId();
346
        if ($item->getType() === 'pages') {
347 20
            $buildRootlineWithPid = $item->getRecordUid();
348 20
        }
349 20
        $rootlineUtility = GeneralUtility::makeInstance(RootlineUtility::class, $buildRootlineWithPid);
350 20
        $rootline = $rootlineUtility->get();
351 20
352
        $pageInRootline = array_filter($rootline, function($page) use ($rootPageId) {
353
            return (int)$page['uid'] === $rootPageId;
354 20
        });
355
        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 20
     * @return Document The Solr document converted from the record
365
     */
366 20
    protected function itemToDocument(Item $item, $language = 0)
367 20
    {
368 20
        $document = null;
369 20
370
        $itemRecord = $this->getFullItemRecord($item, $language);
371
        if (!is_null($itemRecord)) {
372
            $itemIndexingConfiguration = $this->getItemTypeConfiguration($item, $language);
373
            $document = $this->getBaseDocument($item, $itemRecord);
374
            $document = $this->addDocumentFieldsFromTyposcript($document, $itemIndexingConfiguration, $itemRecord);
375
        }
376
377
        return $document;
378 20
    }
379
380 20
    /**
381 20
     * 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 20
     * @return Document A basic Solr document
386 1
     */
387
    protected function getBaseDocument(Item $item, array $itemRecord)
388 1
    {
389
        $type = $item->getType();
390 1
        $rootPageUid = $item->getRootPageUid();
391
        $accessRootLine = $this->getAccessRootline($item);
392
        return $this->documentBuilder->fromRecord($itemRecord, $type, $rootPageUid, $accessRootLine);
393
    }
394 20
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
    protected function getAccessRootline(Item $item)
402
    {
403
        $accessRestriction = '0';
404
        $itemRecord = $item->getRecord();
405 20
406
        // TODO support access restrictions set on storage page
407
408 20
        if (isset($GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['fe_group'])) {
409 20
            $accessRestriction = $itemRecord[$GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['fe_group']];
410
411
            if (empty($accessRestriction)) {
412 20
                // public
413 20
                $accessRestriction = '0';
414 20
            }
415
        }
416
417 20
        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
    protected function processDocuments(Item $item, array $documents)
429 24
    {
430
        // needs to respect the TS settings for the page the item is on, conditions may apply
431 24
        $solrConfiguration = Util::getSolrConfigurationFromPageId($item->getRootPageUid());
432
        $fieldProcessingInstructions = $solrConfiguration->getIndexFieldProcessingInstructionsConfiguration();
433 24
434 4
        // same as in the FE indexer
435 4
        if (is_array($fieldProcessingInstructions)) {
0 ignored issues
show
introduced by
The condition is_array($fieldProcessingInstructions) is always true.
Loading history...
436 2
            $service = GeneralUtility::makeInstance(Service::class);
437
            $service->processDocuments($documents, $fieldProcessingInstructions);
438 2
        }
439 2
440 1
        return $documents;
441
    }
442 1
443 1
    /**
444 1
     * Allows third party extensions to provide additional documents which
445
     * should be indexed for the current item.
446
     *
447 1
     * @param Item $item The item currently being indexed.
448 1
     * @param int $language The language uid currently being indexed.
449 2
     * @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
    protected function getAdditionalDocuments(Item $item, $language, Document $itemDocument)
453
    {
454 21
        $documents = [];
455
456
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['indexItemAddDocuments'])) {
457
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['indexItemAddDocuments'] as $classReference) {
458
                if (!class_exists($classReference)) {
459
                    throw new \InvalidArgumentException('Class does not exits' . $classReference, 1490363487);
460
                }
461
                $additionalIndexer = GeneralUtility::makeInstance($classReference);
462
                if ($additionalIndexer instanceof AdditionalIndexQueueItemIndexer) {
463
                    $additionalDocuments = $additionalIndexer->getAdditionalItemDocuments($item, $language, $itemDocument);
464
465
                    if (is_array($additionalDocuments)) {
466 20
                        $documents = array_merge($documents,
467
                            $additionalDocuments);
468 20
                    }
469
                } else {
470
                    throw new \UnexpectedValueException(
471
                        get_class($additionalIndexer) . ' must implement interface ' . AdditionalIndexQueueItemIndexer::class,
472
                        1326284551
473
                    );
474
                }
475
            }
476
        }
477
        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 20
     * @param array $documents An array of documents to be indexed
487
     * @return array An array of modified documents
488
     */
489
    protected function preAddModifyDocuments(Item $item, $language, array $documents)
490
    {
491
        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 22
                        . '" registered as document modifier in hook
501
							preAddModifyDocuments must implement interface
502 22
							ApacheSolrForTypo3\Solr\IndexQueue\PageIndexerDocumentsModifier',
503
                        1309522677
504 22
                    );
505 22
                }
506 2
            }
507
        }
508
509
        return $documents;
510 22
    }
511
512 22
    // Initialization
513 22
514 22
    /**
515 22
     * Gets the Solr connections applicable for an item.
516
     *
517
     * The connections include the default connection and connections to be used
518 22
     * for translations of an item.
519 22
     *
520
     * @param Item $item An index queue item
521 22
     * @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 22
     */
523
    protected function getSolrConnectionsByItem(Item $item)
524 22
    {
525 21
        $solrConnections = [];
526
527
        $pageId = $item->getRootPageUid();
528 22
        if ($item->getType() === 'pages') {
529 7
            $pageId = $item->getRecordUid();
530
        }
531 22
532
        // Solr configurations possible for this item
533
        $site = $item->getSite();
534
535
        $solrConfigurationsBySite = $this->connectionManager->getConfigurationsBySite($site);
536
        $siteLanguages = [];
537
        foreach ($solrConfigurationsBySite as $solrConfiguration) {
538
            $siteLanguages[] = $solrConfiguration['language'];
539
        }
540
541
        $defaultLanguageUid = $this->getDefaultLanguageUid($item, $site->getRootPage(), $siteLanguages);
542
        $translationOverlays = $this->getTranslationOverlaysWithConfiguredSite($pageId, $site, $defaultLanguageUid, $siteLanguages);
543 22
544
        $defaultConnection = $this->connectionManager->getConnectionByPageId($pageId, 0, $item->getMountPointIdentifier());
545 22
        $translationConnections = $this->getConnectionsForIndexableLanguages($translationOverlays);
546
547 22
        if ($defaultLanguageUid == 0) {
548 7
            $solrConnections[0] = $defaultConnection;
549 7
        }
550
551
        foreach ($translationConnections as $systemLanguageUid => $solrConnection) {
552
            $solrConnections[$systemLanguageUid] = $solrConnection;
553 22
        }
554
        return $solrConnections;
555
    }
556
557
    /**
558
     * Retrieves only translation overlays where a solr site is configured.
559
     *
560
     * @param int $pageId
561
     * @param Site $site
562
     * @param int $defaultLanguageUid
563
     * @param $siteLanguages
564 22
     * @return array
565
     */
566 22
    protected function getTranslationOverlaysWithConfiguredSite($pageId, Site $site, $defaultLanguageUid, $siteLanguages)
567 22
    {
568 1
        $translationOverlays = $this->getTranslationOverlaysForPage($pageId, $site->getSysLanguageMode($defaultLanguageUid));
569 1
570 21
        foreach ($translationOverlays as $key => $translationOverlay) {
571
            if (!in_array($translationOverlay['sys_language_uid'], $siteLanguages)) {
572
                unset($translationOverlays[$key]);
573
            }
574
        }
575 22
576
        return $translationOverlays;
577
    }
578
579
    /**
580
     * @param Item $item An index queue item
581
     * @param array $rootPage
582
     * @param array $siteLanguages
583
     *
584
     * @return int
585
     * @throws \RuntimeException
586
     */
587
    private function getDefaultLanguageUid(Item $item, array $rootPage, array $siteLanguages)
588
    {
589
        $defaultLanguageUid = 0;
590
        if (($rootPage['l18n_cfg'] & 1) == 1 && count($siteLanguages) > 1) {
591
            unset($siteLanguages[array_search('0', $siteLanguages)]);
592 22
            $defaultLanguageUid = $siteLanguages[min(array_keys($siteLanguages))];
593
        } elseif (($rootPage['l18n_cfg'] & 1) == 1 && count($siteLanguages) == 1) {
594 22
            $message = 'Root page ' . (int)$item->getRootPageUid() . ' is set to hide default translation, but no other language is configured!';
595 22
            throw new \RuntimeException($message);
596
        }
597 22
598 22
        return $defaultLanguageUid;
599 22
    }
600 22
601
    /**
602 22
     * Finds the alternative page language overlay records for a page based on
603 6
     * the sys_language_mode.
604
     *
605
     * Possible Language Modes:
606 16
     * 1) content_fallback --> all languages
607 16
     * 2) strict --> available languages with page overlay
608 15
     * 3) ignore --> available languages with page overlay
609 15
     * 4) unknown mode or blank --> all languages
610
     *
611 1
     * @param int $pageId Page ID.
612 1
     * @param string $languageMode
613 1
     * @return array An array of translation overlays (or fake overlays) found for the given page.
614 1
     */
615
    protected function getTranslationOverlaysForPage($pageId, $languageMode)
616
    {
617
        $translationOverlays = [];
618
        $pageId = intval($pageId);
619 22
620
        $languageModes = ['content_fallback', 'strict', 'ignore'];
621
        $hasOverlayMode = in_array($languageMode, $languageModes,
622
            true);
623
        $isContentFallbackMode = ($languageMode === 'content_fallback');
624
625
        if ($hasOverlayMode && !$isContentFallbackMode) {
626
            $translationOverlays = $this->pagesRepository->findTranslationOverlaysByPageId($pageId);
627 15
        } else {
628
            // ! If no sys_language_mode is configured, all languages will be indexed !
629 15
            $languages = $this->getSystemLanguages();
630
            foreach ($languages as $language) {
631
                if ($language['uid'] <= 0) {
632
                    continue;
633
                }
634
                $translationOverlays[] = [
635
                    'pid' => $pageId,
636
                    'l10n_parent' => $pageId,
637
                    'sys_language_uid' => $language['uid'],
638
                ];
639 22
            }
640
        }
641 22
642
        return $translationOverlays;
643 22
    }
644
645 7
    /**
646 7
     * Returns an array of system languages.
647
     *
648
     * @return array
649 7
     */
650 7
    protected function getSystemLanguages()
651 7
    {
652
        return GeneralUtility::makeInstance(TranslationConfigurationProvider::class)->getSystemLanguages();
653
    }
654
655
    /**
656
     * Checks for which languages connections have been configured and returns
657 22
     * these connections.
658
     *
659
     * @param array $translationOverlays An array of translation overlays to check for configured connections.
660
     * @return array An array of ApacheSolrForTypo3\Solr\System\Solr\SolrConnection connections.
661
     */
662
    protected function getConnectionsForIndexableLanguages(array $translationOverlays)
663
    {
664
        $connections = [];
665
666
        foreach ($translationOverlays as $translationOverlay) {
667
            $pageId = $translationOverlay['l10n_parent'];
668
            $languageId = $translationOverlay['sys_language_uid'];
669
670
            try {
671 21
                $connection = $this->connectionManager->getConnectionByPageId($pageId, $languageId);
672
                $connections[$languageId] = $connection;
673 21
            } catch (NoSolrConnectionFoundException $e) {
674 21
                // ignore the exception as we seek only those connections
675 21
                // actually available
676
            }
677 21
        }
678
679
        return $connections;
680
    }
681
682
    // Utility methods
683
684
    // FIXME extract log() and setLogging() to ApacheSolrForTypo3\Solr\IndexQueue\AbstractIndexer
685
    // FIXME extract an interface Tx_Solr_IndexQueue_ItemInterface
686 20
687
    /**
688 20
     * Enables logging dependent on the configuration of the item's site
689 20
     *
690
     * @param Item $item An item being indexed
691
     * @return    void
692
     */
693
    protected function setLogging(Item $item)
694
    {
695
        $solrConfiguration = Util::getSolrConfigurationFromPageId($item->getRootPageUid());
696
        $this->loggingEnabled = $solrConfiguration->getLoggingIndexingQueueOperationsByConfigurationNameWithFallBack(
697
            $item->getIndexingConfigurationName()
698
        );
699
    }
700
701
    /**
702
     * Logs the item and what document was created from it
703
     *
704
     * @param Item $item The item that is being indexed.
705
     * @param array $itemDocuments An array of Solr documents created from the item's data
706
     * @param ResponseAdapter $response The Solr response for the particular index document
707
     */
708
    protected function log(Item $item, array $itemDocuments, ResponseAdapter $response)
709
    {
710
        if (!$this->loggingEnabled) {
711
            return;
712
        }
713
714
        $message = 'Index Queue indexing ' . $item->getType() . ':' . $item->getRecordUid() . ' - ';
715
716
        // preparing data
717
        $documents = [];
718
        foreach ($itemDocuments as $document) {
719
            $documents[] = (array)$document;
720
        }
721
722
        $logData = ['item' => (array)$item, 'documents' => $documents, 'response' => (array)$response];
723
724
        if ($response->getHttpStatus() == 200) {
725
            $severity = SolrLogManager::NOTICE;
726
            $message .= 'Success';
727
        } else {
728
            $severity = SolrLogManager::ERROR;
729
            $message .= 'Failure';
730
731
            $logData['status'] = $response->getHttpStatus();
732
            $logData['status message'] = $response->getHttpStatusMessage();
733
        }
734
735
        $this->logger->log($severity, $message, $logData);
736
    }
737
}
738