Completed
Push — master ( 900c6a...9097a0 )
by Timo
22:24
created

Indexer   F

Complexity

Total Complexity 77

Size/Duplication

Total Lines 722
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 15

Test Coverage

Coverage 57.99%

Importance

Changes 0
Metric Value
wmc 77
lcom 1
cbo 15
dl 0
loc 722
ccs 214
cts 369
cp 0.5799
rs 1.7391
c 0
b 0
f 0

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