Passed
Push — master ( 70b946...2029f9 )
by Timo
18:21
created

Indexer::getSolrConnectionsByItem()   C

Complexity

Conditions 7
Paths 48

Size

Total Lines 46
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 8.464

Importance

Changes 0
Metric Value
dl 0
loc 46
ccs 20
cts 29
cp 0.6897
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 25
nc 48
nop 1
crap 8.464
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 2 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 Apache_Solr_Document;
28
use Apache_Solr_Response;
29
use ApacheSolrForTypo3\Solr\ConnectionManager;
30
use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository;
31
use ApacheSolrForTypo3\Solr\Domain\Variants\IdBuilder;
32
use ApacheSolrForTypo3\Solr\FieldProcessor\Service;
33
use ApacheSolrForTypo3\Solr\NoSolrConnectionFoundException;
34
use ApacheSolrForTypo3\Solr\SolrService;
35
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
36
use ApacheSolrForTypo3\Solr\Util;
37
use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider;
38
use TYPO3\CMS\Backend\Utility\BackendUtility;
39
use TYPO3\CMS\Core\Utility\GeneralUtility;
40
use TYPO3\CMS\Frontend\Page\PageRepository;
41
42
/**
43
 * A general purpose indexer to be used for indexing of any kind of regular
44
 * records like tt_news, tt_address, and so on.
45
 * Specialized indexers can extend this class to handle advanced stuff like
46
 * category resolution in tt_news or file indexing.
47
 *
48
 * @author Ingo Renner <[email protected]>
49
 */
50
class Indexer extends AbstractIndexer
51
{
52
53
    # TODO change to singular $document instead of plural $documents
54
55
    /**
56
     * A Solr service instance to interact with the Solr server
57
     *
58
     * @var SolrService
59
     */
60
    protected $solr;
61
62
    /**
63
     * @var ConnectionManager
64
     */
65
    protected $connectionManager;
66
67
    /**
68
     * Holds options for a specific indexer
69
     *
70
     * @var array
71
     */
72
    protected $options = [];
73
74
    /**
75
     * To log or not to log... #Shakespeare
76
     *
77
     * @var bool
78
     */
79
    protected $loggingEnabled = false;
80
81
    /**
82
     * @var IdBuilder
83
     */
84
    protected $variantIdBuilder;
85
86
    /**
87
     * Cache of the sys_language_overlay information
88
     *
89
     * @var array
90
     */
91
    protected static $sysLanguageOverlay = [];
92
93
    /**
94
     * @var \ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager
95
     */
96
    protected $logger = null;
97
98
    /**
99
     * Constructor
100
     *
101
     * @param array $options array of indexer options
102
     * @param IdBuilder $idBuilder
103
     */
104 17
    public function __construct(array $options = [], IdBuilder $idBuilder = null)
105
    {
106 17
        $this->logger = GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__);
107 17
        $this->options = $options;
108 17
        $this->connectionManager = GeneralUtility::makeInstance(ConnectionManager::class);
109 17
        $this->variantIdBuilder = is_null($idBuilder) ? GeneralUtility::makeInstance(IdBuilder::class) : $idBuilder;
110 17
    }
111
112
    /**
113
     * Indexes an item from the indexing queue.
114
     *
115
     * @param Item $item An index queue item
116
     * @return bool returns true when indexed, false when not
117
     */
118 12
    public function index(Item $item)
119
    {
120 12
        $indexed = true;
121
122 12
        $this->type = $item->getType();
123 12
        $this->setLogging($item);
124
125 12
        $solrConnections = $this->getSolrConnectionsByItem($item);
126
127 12
        foreach ($solrConnections as $systemLanguageUid => $solrConnection) {
128 12
            $this->solr = $solrConnection;
129
130 12
            if (!$this->indexItem($item, $systemLanguageUid)) {
131
                /*
132
                 * A single language voting for "not indexed" should make the whole
133
                 * item count as being not indexed, even if all other languages are
134
                 * indexed.
135
                 * If there is no translation for a single language, this item counts
136
                 * as TRUE since it's not an error which that should make the item
137
                 * being reindexed during another index run.
138
                 */
139 12
                $indexed = false;
140
            }
141
        }
142
143 12
        return $indexed;
144
    }
145
146
    /**
147
     * Creates a single Solr Document for an item in a specific language.
148
     *
149
     * @param Item $item An index queue item to index.
150
     * @param int $language The language to use.
151
     * @return bool TRUE if item was indexed successfully, FALSE on failure
152
     */
153 12
    protected function indexItem(Item $item, $language = 0)
154
    {
155 12
        $itemIndexed = false;
156 12
        $documents = [];
157
158 12
        $itemDocument = $this->itemToDocument($item, $language);
159 12
        if (is_null($itemDocument)) {
160
            /*
161
             * If there is no itemDocument, this means there was no translation
162
             * for this record. This should not stop the current item to count as
163
             * being valid because not-indexing not-translated items is perfectly
164
             * fine.
165
             */
166
            return true;
167
        }
168
169 12
        $documents[] = $itemDocument;
170 12
        $documents = array_merge($documents, $this->getAdditionalDocuments($item, $language, $itemDocument));
171 12
        $documents = $this->processDocuments($item, $documents);
172 12
        $documents = $this->preAddModifyDocuments($item, $language, $documents);
173
174 12
        $response = $this->solr->addDocuments($documents);
175 12
        if ($response->getHttpStatus() == 200) {
176 12
            $itemIndexed = true;
177
        }
178
179 12
        $this->log($item, $documents, $response);
180
181 12
        return $itemIndexed;
182
    }
183
184
    /**
185
     * Gets the full item record.
186
     *
187
     * This general record indexer simply gets the record from the item. Other
188
     * more specialized indexers may provide more data for their specific item
189
     * types.
190
     *
191
     * @param Item $item The item to be indexed
192
     * @param int $language Language Id (sys_language.uid)
193
     * @return array|NULL The full record with fields of data to be used for indexing or NULL to prevent an item from being indexed
194
     */
195 12
    protected function getFullItemRecord(Item $item, $language = 0)
196
    {
197 12
        $rootPageUid = $item->getRootPageUid();
198 12
        $overlayIdentifier = $rootPageUid . '|' . $language;
199 12
        if (!isset(self::$sysLanguageOverlay[$overlayIdentifier])) {
200 12
            Util::initializeTsfe($rootPageUid, $language);
201 12
            self::$sysLanguageOverlay[$overlayIdentifier] = $GLOBALS['TSFE']->sys_language_contentOL;
202
        }
203
204 12
        $itemRecord = $item->getRecord();
205
206 12
        if ($language > 0) {
207 1
            $page = GeneralUtility::makeInstance(PageRepository::class);
208 1
            $page->init(false);
209
210 1
            $itemRecord = $page->getRecordOverlay(
211 1
                $item->getType(),
212
                $itemRecord,
213
                $language,
214 1
                self::$sysLanguageOverlay[$overlayIdentifier]
215
            );
216
        }
217
218 12
        if (!$itemRecord) {
219
            $itemRecord = null;
220
        }
221
222
        /*
223
         * Skip disabled records. This happens if the default language record
224
         * is hidden but a certain translation isn't. Then the default language
225
         * document appears here but must not be indexed.
226
         */
227 12
        if (!empty($GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['disabled'])
228 12
            && $itemRecord[$GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['disabled']]
229
        ) {
230
            $itemRecord = null;
231
        }
232
233
        /*
234
         * Skip translation mismatching records. Sometimes the requested language
235
         * doesn't fit the returned language. This might happen with content fallback
236
         * and is perfectly fine in general.
237
         * But if the requested language doesn't match the returned language and
238
         * the given record has no translation parent, the indexqueue_item most
239
         * probably pointed to a non-translated language record that is dedicated
240
         * to a very specific language. Now we have to avoid indexing this record
241
         * into all language cores.
242
         */
243 12
        $translationOriginalPointerField = 'l10n_parent';
244 12
        if (!empty($GLOBALS['TCA'][$item->getType()]['ctrl']['transOrigPointerField'])) {
245 11
            $translationOriginalPointerField = $GLOBALS['TCA'][$item->getType()]['ctrl']['transOrigPointerField'];
246
        }
247
248 12
        $languageField = $GLOBALS['TCA'][$item->getType()]['ctrl']['languageField'];
249 12
        if ($itemRecord[$translationOriginalPointerField] == 0
250 12
            && self::$sysLanguageOverlay[$overlayIdentifier] != 1
251 12
            && !empty($languageField)
252 12
            && $itemRecord[$languageField] != $language
253 12
            && $itemRecord[$languageField] != '-1'
254
        ) {
255
            $itemRecord = null;
256
        }
257
258 12
        if (!is_null($itemRecord)) {
259 12
            $itemRecord['__solr_index_language'] = $language;
260
        }
261
262 12
        return $itemRecord;
263
    }
264
265
    /**
266
     * Gets the configuration how to process an item's fields for indexing.
267
     *
268
     * @param Item $item An index queue item
269
     * @param int $language Language ID
270
     * @throws \RuntimeException
271
     * @return array Configuration array from TypoScript
272
     */
273 12
    protected function getItemTypeConfiguration(Item $item, $language = 0)
274
    {
275 12
        $indexConfigurationName = $item->getIndexingConfigurationName();
276 12
        $fields = $this->getFieldConfigurationFromItemRecordPage($item, $language, $indexConfigurationName);
277 12
        if (count($fields) === 0) {
278 1
            $fields = $this->getFieldConfigurationFromItemRootPage($item, $language, $indexConfigurationName);
279 1
            if (count($fields) === 0) {
280
                throw new \RuntimeException('The item indexing configuration "' . $item->getIndexingConfigurationName() .
281
                    '" on root page uid ' . $item->getRootPageUid() . ' could not be found!', 1455530112);
282
            }
283
        }
284
285 12
        return $fields;
286
    }
287
288
    /**
289
     * The method retrieves the field configuration of the items record page id (pid).
290
     *
291
     * @param Item $item
292
     * @param integer $language
293
     * @param string $indexConfigurationName
294
     * @return array
295
     */
296 12
    protected function getFieldConfigurationFromItemRecordPage(Item $item, $language, $indexConfigurationName)
297
    {
298
        try {
299 12
            $solrConfiguration = Util::getSolrConfigurationFromPageId($item->getRecordPageId(), true, $language);
300 11
            return $solrConfiguration->getIndexQueueFieldsConfigurationByConfigurationName($indexConfigurationName, []);
301 1
        } catch (\Exception $e) {
302 1
            return [];
303
        }
304
    }
305
306
    /**
307
     * The method returns the field configuration of the items root page id (uid of the related root page).
308
     *
309
     * @param Item $item
310
     * @param integer $language
311
     * @param string $indexConfigurationName
312
     * @return array
313
     */
314 1
    protected function getFieldConfigurationFromItemRootPage(Item $item, $language, $indexConfigurationName)
315
    {
316 1
        $solrConfiguration = Util::getSolrConfigurationFromPageId($item->getRootPageUid(), true, $language);
317 1
        if (empty($solrConfiguration->getIndexQueueAdditionalPageIdsByConfigurationName($indexConfigurationName))) {
318
            return [];
319
        }
320
321 1
        return $solrConfiguration->getIndexQueueFieldsConfigurationByConfigurationName($indexConfigurationName, []);
322
    }
323
324
    /**
325
     * Converts an item array (record) to a Solr document by mapping the
326
     * record's fields onto Solr document fields as configured in TypoScript.
327
     *
328
     * @param Item $item An index queue item
329
     * @param int $language Language Id
330
     * @return Apache_Solr_Document The Solr document converted from the record
331
     */
332 12
    protected function itemToDocument(Item $item, $language = 0)
333
    {
334 12
        $document = null;
335
336 12
        $itemRecord = $this->getFullItemRecord($item, $language);
337 12
        if (!is_null($itemRecord)) {
338 12
            $itemIndexingConfiguration = $this->getItemTypeConfiguration($item, $language);
339 12
            $document = $this->getBaseDocument($item, $itemRecord);
340 12
            $document = $this->addDocumentFieldsFromTyposcript($document, $itemIndexingConfiguration, $itemRecord);
341
        }
342
343 12
        return $document;
344
    }
345
346
    /**
347
     * Creates a Solr document with the basic / core fields set already.
348
     *
349
     * @param Item $item The item to index
350
     * @param array $itemRecord The record to use to build the base document
351
     * @return Apache_Solr_Document A basic Solr document
352
     */
353 12
    protected function getBaseDocument(Item $item, array $itemRecord)
354
    {
355 12
        $siteRepository = GeneralUtility::makeInstance(SiteRepository::class);
356 12
        $site = $siteRepository->getSiteByRootPageId($item->getRootPageUid());
357
358 12
        $document = GeneralUtility::makeInstance(Apache_Solr_Document::class);
359
        /* @var $document Apache_Solr_Document */
360
361
        // required fields
362 12
        $document->setField('id', Util::getDocumentId($item->getType(), $site->getRootPageId(), $itemRecord['uid']));
363 12
        $document->setField('type', $item->getType());
364 12
        $document->setField('appKey', 'EXT:solr');
365
366
        // site, siteHash
367 12
        $document->setField('site', $site->getDomain());
368 12
        $document->setField('siteHash', $site->getSiteHash());
369
370
        // uid, pid
371 12
        $document->setField('uid', $itemRecord['uid']);
372 12
        $document->setField('pid', $itemRecord['pid']);
373
374
        // variantId
375 12
        $variantId = $this->variantIdBuilder->buildFromTypeAndUid($item->getType(), $itemRecord['uid']);
376 12
        $document->setField('variantId', $variantId);
377
378
        // created, changed
379 12
        if (!empty($GLOBALS['TCA'][$item->getType()]['ctrl']['crdate'])) {
380 12
            $document->setField('created',
381 12
                $itemRecord[$GLOBALS['TCA'][$item->getType()]['ctrl']['crdate']]);
382
        }
383 12
        if (!empty($GLOBALS['TCA'][$item->getType()]['ctrl']['tstamp'])) {
384 12
            $document->setField('changed',
385 12
                $itemRecord[$GLOBALS['TCA'][$item->getType()]['ctrl']['tstamp']]);
386
        }
387
388
        // access, endtime
389 12
        $document->setField('access', $this->getAccessRootline($item));
390 12
        if (!empty($GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['endtime'])
391 12
            && $itemRecord[$GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['endtime']] != 0
392
        ) {
393
            $document->setField('endtime',
394
                $itemRecord[$GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['endtime']]);
395
        }
396
397 12
        return $document;
398
    }
399
400
    /**
401
     * Generates an Access Rootline for an item.
402
     *
403
     * @param Item $item Index Queue item to index.
404
     * @return string The Access Rootline for the item
405
     */
406 12
    protected function getAccessRootline(Item $item)
407
    {
408 12
        $accessRestriction = '0';
409 12
        $itemRecord = $item->getRecord();
410
411
        // TODO support access restrictions set on storage page
412
413 12
        if (isset($GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['fe_group'])) {
414 1
            $accessRestriction = $itemRecord[$GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['fe_group']];
415
416 1
            if (empty($accessRestriction)) {
417
                // public
418 1
                $accessRestriction = '0';
419
            }
420
        }
421
422 12
        return 'r:' . $accessRestriction;
423
    }
424
425
    /**
426
     * Sends the documents to the field processing service which takes care of
427
     * manipulating fields as defined in the field's configuration.
428
     *
429
     * @param Item $item An index queue item
430
     * @param array $documents An array of Apache_Solr_Document objects to manipulate.
431
     * @return array Array of manipulated Apache_Solr_Document objects.
432
     */
433 12
    protected function processDocuments(Item $item, array $documents)
434
    {
435
        // needs to respect the TS settings for the page the item is on, conditions may apply
436 12
        $solrConfiguration = Util::getSolrConfigurationFromPageId($item->getRootPageUid());
437 12
        $fieldProcessingInstructions = $solrConfiguration->getIndexFieldProcessingInstructionsConfiguration();
438
439
        // same as in the FE indexer
440 12
        if (is_array($fieldProcessingInstructions)) {
441 12
            $service = GeneralUtility::makeInstance(Service::class);
442 12
            $service->processDocuments(
443
                $documents,
444
                $fieldProcessingInstructions
445
            );
446
        }
447
448 12
        return $documents;
449
    }
450
451
    /**
452
     * Allows third party extensions to provide additional documents which
453
     * should be indexed for the current item.
454
     *
455
     * @param Item $item The item currently being indexed.
456
     * @param int $language The language uid currently being indexed.
457
     * @param Apache_Solr_Document $itemDocument The document representing the item for the given language.
458
     * @return array An array of additional Apache_Solr_Document objects to index.
459
     */
460 16
    protected function getAdditionalDocuments(
461
        Item $item,
462
        $language,
463
        Apache_Solr_Document $itemDocument
464
    ) {
465 16
        $documents = [];
466
467 16
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['indexItemAddDocuments'])) {
468 4
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['indexItemAddDocuments'] as $classReference) {
469 4
                if (!class_exists($classReference)) {
470 2
                    throw new \InvalidArgumentException('Class does not exits' . $classReference, 1490363487);
471
                }
472 2
                $additionalIndexer = GeneralUtility::makeInstance($classReference);
473 2
                if ($additionalIndexer instanceof AdditionalIndexQueueItemIndexer) {
474 1
                    $additionalDocuments = $additionalIndexer->getAdditionalItemDocuments($item,
475
                        $language, $itemDocument);
476
477 1
                    if (is_array($additionalDocuments)) {
478 1
                        $documents = array_merge($documents,
479
                            $additionalDocuments);
480
                    }
481
                } else {
482 1
                    throw new \UnexpectedValueException(
483 1
                        get_class($additionalIndexer) . ' must implement interface ' . AdditionalIndexQueueItemIndexer::class,
484 2
                        1326284551
485
                    );
486
                }
487
            }
488
        }
489 13
        return $documents;
490
    }
491
492
    /**
493
     * Provides a hook to manipulate documents right before they get added to
494
     * the Solr index.
495
     *
496
     * @param Item $item The item currently being indexed.
497
     * @param int $language The language uid of the documents
498
     * @param array $documents An array of documents to be indexed
499
     * @return array An array of modified documents
500
     */
501 12
    protected function preAddModifyDocuments(
502
        Item $item,
503
        $language,
504
        array $documents
505
    ) {
506 12
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['preAddModifyDocuments'])) {
507
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['preAddModifyDocuments'] as $classReference) {
508
                $documentsModifier = GeneralUtility::getUserObj($classReference);
509
510
                if ($documentsModifier instanceof PageIndexerDocumentsModifier) {
511
                    $documents = $documentsModifier->modifyDocuments($item,
512
                        $language, $documents);
513
                } else {
514
                    throw new \RuntimeException(
515
                        'The class "' . get_class($documentsModifier)
516
                        . '" registered as document modifier in hook
517
							preAddModifyDocuments must implement interface
518
							ApacheSolrForTypo3\Solr\IndexQueue\PageIndexerDocumentsModifier',
519
                        1309522677
520
                    );
521
                }
522
            }
523
        }
524
525 12
        return $documents;
526
    }
527
528
    // Initialization
529
530
    /**
531
     * Gets the Solr connections applicable for an item.
532
     *
533
     * The connections include the default connection and connections to be used
534
     * for translations of an item.
535
     *
536
     * @param Item $item An index queue item
537
     * @return array An array of ApacheSolrForTypo3\Solr\SolrService connections, the array's keys are the sys_language_uid of the language of the connection
538
     */
539 13
    protected function getSolrConnectionsByItem(Item $item)
540
    {
541 13
        $solrConnections = [];
542
543 13
        $pageId = $item->getRootPageUid();
544 13
        if ($item->getType() == 'pages') {
545 2
            $pageId = $item->getRecordUid();
546
        }
547
548
        // Solr configurations possible for this item
549 13
        $site = $item->getSite();
550 13
        $solrConfigurationsBySite = $this->connectionManager->getConfigurationsBySite($site);
551
552 13
        $siteLanguages = [];
553 13
        foreach ($solrConfigurationsBySite as $solrConfiguration) {
554 13
            $siteLanguages[] = $solrConfiguration['language'];
555
        }
556
557 13
        $defaultLanguageUid = $this->getDefaultLanguageUid($item, $site->getRootPage(), $siteLanguages);
558 13
559 1
        $translationOverlays = $this->getTranslationOverlaysForPage(
560
            $pageId,
561
            $site->getSysLanguageMode($defaultLanguageUid)
562 1
        );
563
564
        foreach ($translationOverlays as $key => $translationOverlay) {
565
            if (!in_array($translationOverlay['sys_language_uid'],
566 13
                $siteLanguages)
567 13
            ) {
568
                unset($translationOverlays[$key]);
569 13
            }
570 13
        }
571 1
572
        $defaultConnection = $this->connectionManager->getConnectionByPageId($pageId, 0, $item->getMountPointIdentifier());
573
        $translationConnections = $this->getConnectionsForIndexableLanguages($translationOverlays);
574 13
575
        if ($defaultLanguageUid == 0) {
576
            $solrConnections[0] = $defaultConnection;
577
        }
578
579
        foreach ($translationConnections as $systemLanguageUid => $solrConnection) {
580
            $solrConnections[$systemLanguageUid] = $solrConnection;
581
        }
582
583
        return $solrConnections;
584
    }
585
586
    /**
587
     * @param Item $item An index queue item
588
     * @param array $rootPage
589
     * @param array $siteLanguages
590
     *
591 13
     * @return int
592
     * @throws \Apache_Solr_Exception
593 13
     */
594 13
    private function getDefaultLanguageUid(Item $item, array $rootPage, array $siteLanguages)
595
    {
596 13
        $defaultLanguageUid = 0;
597 13
        if (($rootPage['l18n_cfg'] & 1) == 1 && count($siteLanguages) > 1) {
598 13
            unset($siteLanguages[array_search('0', $siteLanguages)]);
599 13
            $defaultLanguageUid = $siteLanguages[min(array_keys($siteLanguages))];
600
        } elseif (($rootPage['l18n_cfg'] & 1) == 1 && count($siteLanguages) == 1) {
601 13
            throw new \Apache_Solr_Exception('Root page ' .
602 1
                                             $item->getRootPageUid() .
603 1
                                             ' is set to hide default translation, but no other language is configured!');
604 1
        }
605 1
606 1
        return $defaultLanguageUid;
607 1
    }
608
609
    /**
610
     * Finds the alternative page language overlay records for a page based on
611 12
     * the sys_language_mode.
612
     *
613 12
     * Possible Language Modes:
614 12
     * 1) content_fallback --> all languages
615 12
     * 2) strict --> available languages with page overlay
616
     * 3) ignore --> available languages with page overlay
617
     * 4) unknown mode or blank --> all languages
618
     *
619
     * @param int $pageId Page ID.
620
     * @param string $languageMode
621
     * @return array An array of translation overlays (or fake overlays) found for the given page.
622
     */
623
    protected function getTranslationOverlaysForPage($pageId, $languageMode)
624 13
    {
625
        $translationOverlays = [];
626
        $pageId = intval($pageId);
627
628
        $languageModes = ['content_fallback', 'strict', 'ignore'];
629
        $hasOverlayMode = in_array($languageMode, $languageModes,
630
            true);
631
        $isContentFallbackMode = ($languageMode === 'content_fallback');
632 12
633
        if ($hasOverlayMode && !$isContentFallbackMode) {
634 12
            $translationOverlays = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
635
                'pid, sys_language_uid',
636
                'pages_language_overlay',
637
                'pid = ' . $pageId
638
                . BackendUtility::deleteClause('pages_language_overlay')
639
                . BackendUtility::BEenableFields('pages_language_overlay')
640
            );
641
        } else {
642
            // ! If no sys_language_mode is configured, all languages will be indexed !
643
            $languages = $this->getSystemLanguages();
644 13
645
            foreach ($languages as $language) {
646
                if ($language['uid'] <= 0) {
647 13
                    continue;
648
                }
649 13
                $translationOverlays[] = [
650 1
                    'pid' => $pageId,
651 1
                    'sys_language_uid' => $language['uid'],
652
                ];
653
            }
654 1
        }
655
656 1
        return $translationOverlays;
657 1
    }
658
659
    /**
660
     * Returns an array of system languages.
661
     *
662
     * @return array
663 13
     */
664
    protected function getSystemLanguages()
665
    {
666
        return GeneralUtility::makeInstance(TranslationConfigurationProvider::class)->getSystemLanguages();
667
    }
668
669
    /**
670
     * Checks for which languages connections have been configured and returns
671
     * these connections.
672
     *
673
     * @param array $translationOverlays An array of translation overlays to check for configured connections.
674
     * @return array An array of ApacheSolrForTypo3\Solr\SolrService connections.
675
     */
676
    protected function getConnectionsForIndexableLanguages(
677 13
        array $translationOverlays
678
    ) {
679 13
        $connections = [];
680 13
681 13
        foreach ($translationOverlays as $translationOverlay) {
682
            $pageId = $translationOverlay['pid'];
683 13
            $languageId = $translationOverlay['sys_language_uid'];
684
685
            try {
686
                $connection = $this->connectionManager->getConnectionByPageId($pageId,
687
                    $languageId);
688
                $connections[$languageId] = $connection;
689
            } catch (NoSolrConnectionFoundException $e) {
690
                // ignore the exception as we seek only those connections
691
                // actually available
692 12
            }
693
        }
694
695
        return $connections;
696
    }
697 12
698 12
    // Utility methods
699
700
    // FIXME extract log() and setLogging() to ApacheSolrForTypo3\Solr\IndexQueue\AbstractIndexer
701
    // FIXME extract an interface Tx_Solr_IndexQueue_ItemInterface
702
703
    /**
704
     * Enables logging dependent on the configuration of the item's site
705
     *
706
     * @param Item $item An item being indexed
707
     * @return    void
708
     */
709
    protected function setLogging(Item $item)
710
    {
711
        $solrConfiguration = Util::getSolrConfigurationFromPageId($item->getRootPageUid());
712
        $this->loggingEnabled = $solrConfiguration->getLoggingIndexingQueueOperationsByConfigurationNameWithFallBack(
713
            $item->getIndexingConfigurationName()
714
        );
715
    }
716
717
    /**
718
     * Logs the item and what document was created from it
719
     *
720
     * @param Item $item The item that is being indexed.
721
     * @param array $itemDocuments An array of Solr documents created from the item's data
722
     * @param Apache_Solr_Response $response The Solr response for the particular index document
723
     */
724
    protected function log(
725
        Item $item,
726
        array $itemDocuments,
727
        Apache_Solr_Response $response
728
    ) {
729
        if (!$this->loggingEnabled) {
730
            return;
731
        }
732
733
        $message = 'Index Queue indexing ' . $item->getType() . ':'
734
            . $item->getRecordUid() . ' - ';
735
736
        // preparing data
737
        $documents = [];
738
        foreach ($itemDocuments as $document) {
739
            $documents[] = (array)$document;
740
        }
741
742
        $logData = [
743
            'item' => (array)$item,
744
            'documents' => $documents,
745
            'response' => (array)$response
746
        ];
747
748
        if ($response->getHttpStatus() == 200) {
749
            $severity = SolrLogManager::NOTICE;
750
            $message .= 'Success';
751
        } else {
752
            $severity = SolrLogManager::ERROR;
753
            $message .= 'Failure';
754
755
            $logData['status'] = $response->getHttpStatus();
756
            $logData['status message'] = $response->getHttpStatusMessage();
757
        }
758
759
        $this->logger->log(
760
            $severity,
761
            $message,
762
            $logData
763
        );
764
    }
765
}
766