Passed
Push — master ( b3aa46...9fc6fb )
by Timo
19:53
created

Indexer   F

Complexity

Total Complexity 76

Size/Duplication

Total Lines 713
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 15

Test Coverage

Coverage 55.21%

Importance

Changes 0
Metric Value
wmc 76
lcom 1
cbo 15
dl 0
loc 713
ccs 196
cts 355
cp 0.5521
rs 1.791
c 0
b 0
f 0

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 2
B index() 0 27 3
B indexItem() 0 30 3
C getFullItemRecord() 0 49 10
A getItemRecordOverlayed() 0 16 3
A getItemTypeConfiguration() 0 14 3
A getFieldConfigurationFromItemRecordPage() 0 9 2
A getFieldConfigurationFromItemRootPage() 0 9 2
A itemToDocument() 0 13 2
B getBaseDocument() 0 46 5
A getAccessRootline() 0 18 3
A processDocuments() 0 17 2
B getAdditionalDocuments() 0 31 6
B preAddModifyDocuments() 0 26 4
C getSolrConnectionsByItem() 0 46 7
B getDefaultLanguageUid() 0 14 5
B getTranslationOverlaysForPage() 0 34 5
A getSystemLanguages() 0 4 1
A getConnectionsForIndexableLanguages() 0 21 3
A setLogging() 0 7 1
B log() 0 41 4

How to fix   Complexity   

Complex Class

Complex classes like Indexer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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

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

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