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