From a2018b1b03e0dbf088cd1a50646d33a2db87adf8 Mon Sep 17 00:00:00 2001 From: Mathias Brodala Date: Thu, 8 Oct 2020 18:33:52 +0200 Subject: [PATCH 1/2] [TASK] Use callback to restore environment after indexer run --- Classes/Service/IndexingService.php | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/Classes/Service/IndexingService.php b/Classes/Service/IndexingService.php index 09b57454..1f974abb 100644 --- a/Classes/Service/IndexingService.php +++ b/Classes/Service/IndexingService.php @@ -228,13 +228,13 @@ protected function runIndexers(): void $this->logger->debug(sprintf('Indexing language "%s"', $language)); $environment = ExtconfService::getIndexEnvironment(ExtconfService::getIndex($language)); - $originalEnvironment = $this->applyEnvironment($environment); + $restoreEnvironment = $this->applyEnvironment($environment); foreach ($indexers as $indexer) { $this->runSingleIndexer($indexer); } - $this->applyEnvironment($originalEnvironment); + $restoreEnvironment(); } else { $this->logger->warning(sprintf('No indexers found for language "%s", doing nothing', $language)); } @@ -282,23 +282,21 @@ protected function runSingleIndexer(IndexerInterface $indexer): void /** * Apply the given environment, e.g. language and locale * - * @return array the original environment to be restored with another call + * @return \Closure callback to restore the original environment */ - protected function applyEnvironment(array $environment): array + protected function applyEnvironment(array $environment): \Closure { - $originalEnvironment = []; + $originalUserLanguage = $GLOBALS['BE_USER']->uc['lang']; + $originalLocale = setlocale(LC_ALL, '0'); + $restoreEnvironment = function () use ($originalUserLanguage, $originalLocale): void { + $GLOBALS['BE_USER']->uc['lang'] = $originalUserLanguage; + setlocale(LC_ALL, $originalLocale); + }; - if (!empty($environment['language'])) { - $originalEnvironment['language'] = $GLOBALS['BE_USER']->uc['lang']; - $GLOBALS['BE_USER']->uc['lang'] = $environment['language']; - } - - if (!empty($environment['locale'])) { - $originalEnvironment['locale'] = setlocale(LC_ALL, '0'); - setlocale(LC_ALL, $environment['locale']); - } + $GLOBALS['BE_USER']->uc['lang'] = $environment['language']; + setlocale(LC_ALL, $environment['locale']); - return $originalEnvironment; + return $restoreEnvironment; } /** From 583a8d160e7571a58d16631ef4b0683f55fe2716 Mon Sep 17 00:00:00 2001 From: Mathias Brodala Date: Thu, 8 Oct 2020 18:40:19 +0200 Subject: [PATCH 2/2] [TASK] Set language aspect for indexing This is essential for preview rendering which uses Extbase for retrieving objects. Here we need to set the language aspect since this is used by the Typo3QuerySettings to prepare language overlays. Also reset the Extbase persistence between indexing for a language to ensure objects are retrieved with the correct language. --- Classes/Service/IndexingService.php | 56 +++++++++++++++++-- .../Functional/AbstractElasticsearchTest.php | 21 +++++++ .../Classes/Domain/Model/Content.php | 24 ++++++++ .../Domain/Repository/ContentRepository.php | 10 ++++ .../Preview/ContentPreviewRenderer.php | 30 ++++++++++ .../Extbase/Persistence/Classes.php | 8 +++ .../extbase_l10n_test/composer.json | 8 +++ .../extbase_l10n_test/ext_emconf.php | 11 ++++ .../ext_typoscript_setup.txt | 11 ++++ .../Service/IndexingServiceTest.php | 44 +++++++++++++++ composer.json | 1 + phpstan.neon | 4 +- 12 files changed, 221 insertions(+), 7 deletions(-) create mode 100644 Tests/Functional/Fixtures/Extensions/extbase_l10n_test/Classes/Domain/Model/Content.php create mode 100644 Tests/Functional/Fixtures/Extensions/extbase_l10n_test/Classes/Domain/Repository/ContentRepository.php create mode 100644 Tests/Functional/Fixtures/Extensions/extbase_l10n_test/Classes/Preview/ContentPreviewRenderer.php create mode 100644 Tests/Functional/Fixtures/Extensions/extbase_l10n_test/Configuration/Extbase/Persistence/Classes.php create mode 100644 Tests/Functional/Fixtures/Extensions/extbase_l10n_test/composer.json create mode 100644 Tests/Functional/Fixtures/Extensions/extbase_l10n_test/ext_emconf.php create mode 100644 Tests/Functional/Fixtures/Extensions/extbase_l10n_test/ext_typoscript_setup.txt diff --git a/Classes/Service/IndexingService.php b/Classes/Service/IndexingService.php index 1f974abb..35ddaa1f 100644 --- a/Classes/Service/IndexingService.php +++ b/Classes/Service/IndexingService.php @@ -9,7 +9,11 @@ use PAGEmachine\Searchable\IndexManager; use PAGEmachine\Searchable\PipelineManager; use PAGEmachine\Searchable\Service\ExtconfService; +use TYPO3\CMS\Core\Context\Context; +use TYPO3\CMS\Core\Context\LanguageAspect; use TYPO3\CMS\Core\Log\LogManager; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface; use TYPO3\CMS\Extbase\SignalSlot\Dispatcher; final class IndexingService @@ -27,6 +31,19 @@ public function injectIndexerFactory(IndexerFactory $indexerFactory): void $this->indexerFactory = $indexerFactory; } + /** + * @var PersistenceManagerInterface $persistenceManager + */ + protected $persistenceManager; + + /** + * @param PersistenceManagerInterface $persistenceManager + */ + public function injectPersistenceManager(PersistenceManagerInterface $persistenceManager): void + { + $this->persistenceManager = $persistenceManager; + } + /** * @var Dispatcher $signalDispatcher */ @@ -228,13 +245,14 @@ protected function runIndexers(): void $this->logger->debug(sprintf('Indexing language "%s"', $language)); $environment = ExtconfService::getIndexEnvironment(ExtconfService::getIndex($language)); - $restoreEnvironment = $this->applyEnvironment($environment); + $restoreEnvironment = $this->applyEnvironment((int)$language, $environment); foreach ($indexers as $indexer) { $this->runSingleIndexer($indexer); } $restoreEnvironment(); + $this->resetPersistence(); } else { $this->logger->warning(sprintf('No indexers found for language "%s", doing nothing', $language)); } @@ -284,21 +302,47 @@ protected function runSingleIndexer(IndexerInterface $indexer): void * * @return \Closure callback to restore the original environment */ - protected function applyEnvironment(array $environment): \Closure + protected function applyEnvironment(int $languageUid, array $environment): \Closure { $originalUserLanguage = $GLOBALS['BE_USER']->uc['lang']; $originalLocale = setlocale(LC_ALL, '0'); - $restoreEnvironment = function () use ($originalUserLanguage, $originalLocale): void { - $GLOBALS['BE_USER']->uc['lang'] = $originalUserLanguage; - setlocale(LC_ALL, $originalLocale); - }; $GLOBALS['BE_USER']->uc['lang'] = $environment['language']; setlocale(LC_ALL, $environment['locale']); + if (class_exists(Context::class)) { + $context = GeneralUtility::makeInstance(Context::class); + $originalLanguageAspect = $context->getAspect('language'); + + $restoreEnvironment = function () use ($originalUserLanguage, $originalLocale, $originalLanguageAspect): void { + $GLOBALS['BE_USER']->uc['lang'] = $originalUserLanguage; + setlocale(LC_ALL, $originalLocale); + + $context = GeneralUtility::makeInstance(Context::class); + $context->setAspect('language', $originalLanguageAspect); + }; + + $context->setAspect('language', new LanguageAspect($languageUid)); + } else { // TYPO3v8 + $restoreEnvironment = function () use ($originalUserLanguage, $originalLocale): void { + $GLOBALS['BE_USER']->uc['lang'] = $originalUserLanguage; + setlocale(LC_ALL, $originalLocale); + }; + } + return $restoreEnvironment; } + /** + * Reset the Extbase persistence + * + * This is essential e.g. for retrieving objects once per language. + */ + private function resetPersistence(): void + { + $this->persistenceManager->clearState(); + } + /** * Checks if Elasticsearch is online * diff --git a/Tests/Functional/AbstractElasticsearchTest.php b/Tests/Functional/AbstractElasticsearchTest.php index a50084cf..6f242c59 100644 --- a/Tests/Functional/AbstractElasticsearchTest.php +++ b/Tests/Functional/AbstractElasticsearchTest.php @@ -7,7 +7,9 @@ use Nimut\TestingFramework\TestCase\FunctionalTestCase; use PAGEmachine\Searchable\Connection; use PAGEmachine\Searchable\Indexer\PagesIndexer; +use PAGEmachine\Searchable\Indexer\TcaIndexer; use PAGEmachine\Searchable\Service\IndexingService; +use Pagemachine\SearchableExtbaseL10nTest\Preview\ContentPreviewRenderer; use TYPO3\CMS\Core\Configuration\SiteConfiguration; use TYPO3\CMS\Core\Core\Bootstrap; use TYPO3\CMS\Core\Core\Environment; @@ -27,6 +29,7 @@ abstract class AbstractElasticsearchTest extends FunctionalTestCase */ protected $testExtensionsToLoad = [ 'typo3conf/ext/searchable', + 'typo3conf/ext/searchable/Tests/Functional/Fixtures/Extensions/extbase_l10n_test', ]; /** @@ -81,6 +84,24 @@ protected function setUp() ], ], ], + 'content' => [ + 'className' => TcaIndexer::class, + 'config' => [ + 'type' => 'content', + 'collector' => [ + 'config' => [ + 'table' => 'tt_content', + 'pid' => 1, + 'fields' => [ + 'header', + ], + ], + ], + 'preview' => [ + 'className' => ContentPreviewRenderer::class, + ], + ], + ], ], ] ); diff --git a/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/Classes/Domain/Model/Content.php b/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/Classes/Domain/Model/Content.php new file mode 100644 index 00000000..9c920670 --- /dev/null +++ b/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/Classes/Domain/Model/Content.php @@ -0,0 +1,24 @@ +header; + } + + public function setHeader(string $header): void + { + $this->header = $header; + } +} diff --git a/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/Classes/Domain/Repository/ContentRepository.php b/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/Classes/Domain/Repository/ContentRepository.php new file mode 100644 index 00000000..e33816bb --- /dev/null +++ b/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/Classes/Domain/Repository/ContentRepository.php @@ -0,0 +1,10 @@ +get(ContentRepository::class); + $content = $repository->findByIdentifier($record['uid']); + $preview = sprintf( + 'Preview: %s [%d]', + $content->getHeader(), + $content->_getProperty('_localizedUid') + ); + + return $preview; + } +} diff --git a/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/Configuration/Extbase/Persistence/Classes.php b/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/Configuration/Extbase/Persistence/Classes.php new file mode 100644 index 00000000..bab1a2b2 --- /dev/null +++ b/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/Configuration/Extbase/Persistence/Classes.php @@ -0,0 +1,8 @@ + [ + 'tableName' => 'tt_content', + ], +]; diff --git a/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/composer.json b/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/composer.json new file mode 100644 index 00000000..bb08ac8d --- /dev/null +++ b/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/composer.json @@ -0,0 +1,8 @@ +{ + "name": "pagemachine/searchable-extbase-l10n-test", + "autoload": { + "psr-4": { + "Pagemachine\\SearchableExtbaseL10nTest\\": "Classes/" + } + } +} diff --git a/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/ext_emconf.php b/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/ext_emconf.php new file mode 100644 index 00000000..61009b3e --- /dev/null +++ b/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/ext_emconf.php @@ -0,0 +1,11 @@ + 'Extbase Localization Test', + 'version' => '0.0.0', + 'constraints' => [ + 'depends' => [ + 'typo3' => '0.0.0', + ], + ], +]; diff --git a/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/ext_typoscript_setup.txt b/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/ext_typoscript_setup.txt new file mode 100644 index 00000000..4ee5abdc --- /dev/null +++ b/Tests/Functional/Fixtures/Extensions/extbase_l10n_test/ext_typoscript_setup.txt @@ -0,0 +1,11 @@ +config.tx_extbase { + persistence { + classes { + Pagemachine\SearchableExtbaseL10nTest\Domain\Model\Content { + mapping { + tableName = tt_content + } + } + } + } +} diff --git a/Tests/Functional/Service/IndexingServiceTest.php b/Tests/Functional/Service/IndexingServiceTest.php index 39f2127a..3234d005 100644 --- a/Tests/Functional/Service/IndexingServiceTest.php +++ b/Tests/Functional/Service/IndexingServiceTest.php @@ -126,6 +126,50 @@ public function indexesRecordTranslations(): void ], 1); } + /** + * @test + */ + public function appliesLanguageForRecordTranslationIndexing(): void + { + if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '9', '<')) { + $this->markTestSkipped('TYPO3v9+ only'); + } + + $this->getDatabaseConnection()->insertArray('tt_content', [ + 'uid' => 1, + 'pid' => 1, + 'header' => 'Test content', + ]); + $this->getDatabaseConnection()->insertArray('tt_content', [ + 'uid' => 2, + 'pid' => 1, + 'l18n_parent' => 1, // [sic!] + 'sys_language_uid' => 1, + 'header' => 'Translated test content', + ]); + + $this->assertIndexEmpty(0); + $this->assertIndexEmpty(1); + + $this->indexingService->setup(); + $this->indexingService->indexFull('content'); + + $this->assertDocumentInIndex([ + 'uid' => 1, + 'header' => 'Test content', + 'searchable_meta' => [ + 'preview' => 'Preview: Test content [1]', + ], + ], 0); + $this->assertDocumentInIndex([ + 'uid' => 1, + 'header' => 'Translated test content', + 'searchable_meta' => [ + 'preview' => 'Preview: Translated test content [2]', + ], + ], 1); + } + /** * @test */ diff --git a/composer.json b/composer.json index ecda3088..2704a2c2 100644 --- a/composer.json +++ b/composer.json @@ -52,6 +52,7 @@ "autoload-dev": { "psr-4": { "PAGEmachine\\Searchable\\Tests\\": "Tests/", + "Pagemachine\\SearchableExtbaseL10nTest\\": "Tests/Functional/Fixtures/Extensions/extbase_l10n_test/Classes/", "TYPO3\\CMS\\Core\\Tests\\": "vendor/typo3/cms/typo3/sysext/core/Tests/" }, "classmap": [ diff --git a/phpstan.neon b/phpstan.neon index 93405d1c..c79627cd 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -16,7 +16,9 @@ parameters: - message: '#Psr\\Http\\Server\\MiddlewareInterface#' path: Classes/Middleware/UriBuilder.php - message: '#TYPO3\\CMS\\Core\\Context\\.+#' - path: Classes/ViewHelpers/SiteLanguageViewHelper.php + paths: + - Classes/Service/IndexingService.php + - Classes/ViewHelpers/SiteLanguageViewHelper.php - message: '#TYPO3\\CMS\\Core\\Database\\PostProcessQueryHookInterface#' paths: - ext_localconf.php