From a469f63aa651a2a6294846103740ca4ce1b505c2 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Tue, 13 Mar 2018 10:51:59 +0100 Subject: [PATCH 01/19] TASK: Cleanup differences We have some small differences between develop and support/76 branch. This differences are just CGL and should not exist. --- Classes/Domain/Index/AbstractIndexer.php | 4 ---- Classes/Domain/Index/TcaIndexer/RelationResolver.php | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Classes/Domain/Index/AbstractIndexer.php b/Classes/Domain/Index/AbstractIndexer.php index d264b08..6644716 100644 --- a/Classes/Domain/Index/AbstractIndexer.php +++ b/Classes/Domain/Index/AbstractIndexer.php @@ -68,10 +68,6 @@ abstract class AbstractIndexer implements IndexerInterface $this->identifier = $identifier; } - /** - * @param ConnectionInterface $connection - * @param ConfigurationContainerInterface $configuration - */ public function __construct(ConnectionInterface $connection, ConfigurationContainerInterface $configuration) { $this->connection = $connection; diff --git a/Classes/Domain/Index/TcaIndexer/RelationResolver.php b/Classes/Domain/Index/TcaIndexer/RelationResolver.php index fb19a4f..6af330e 100644 --- a/Classes/Domain/Index/TcaIndexer/RelationResolver.php +++ b/Classes/Domain/Index/TcaIndexer/RelationResolver.php @@ -97,7 +97,7 @@ class RelationResolver implements Singleton return array_map('trim', explode(',', $value)); } - protected function getUtilityForMode(): string + protected function getUtilityForMode() : string { if (TYPO3_MODE === 'BE') { return BackendUtility::class; From ba19537f4e9e10800a41a1a7a12f3243a9a54e92 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Tue, 13 Mar 2018 11:02:29 +0100 Subject: [PATCH 02/19] TASK: Migrate existing 7.6 features We had some features in 7.6 support which we didn't merge up yet. Mostly very small bug fixes or more helpful logging and processing of elasticsearch options. But also adding images of content elements while indexing pages. --- .../Connection/Elasticsearch/IndexFactory.php | 15 +++-- .../Domain/Index/TcaIndexer/PagesIndexer.php | 55 ++++++++++++++++--- Classes/Domain/Search/QueryFactory.php | 14 +++-- 3 files changed, 66 insertions(+), 18 deletions(-) diff --git a/Classes/Connection/Elasticsearch/IndexFactory.php b/Classes/Connection/Elasticsearch/IndexFactory.php index 8b57783..c56892a 100644 --- a/Classes/Connection/Elasticsearch/IndexFactory.php +++ b/Classes/Connection/Elasticsearch/IndexFactory.php @@ -53,7 +53,10 @@ class IndexFactory implements Singleton $index = $connection->getClient()->getIndex('typo3content'); if ($index->exists() === false) { - $index->create($this->getConfigurationFor($documentType)); + $config = $this->getConfigurationFor($documentType); + $this->logger->debug(sprintf('Create index %s.', $documentType), [$documentType, $config]); + $index->create($config); + $this->logger->debug(sprintf('Created index %s.', $documentType), [$documentType]); } return $index; @@ -64,9 +67,11 @@ class IndexFactory implements Singleton try { $configuration = $this->configuration->get('indexing.' . $documentType . '.index'); - if (isset($configuration['analysis']['analyzer'])) { - foreach ($configuration['analysis']['analyzer'] as $key => $analyzer) { - $configuration['analysis']['analyzer'][$key] = $this->prepareAnalyzerConfiguration($analyzer); + foreach (['analyzer', 'filter'] as $optionsToExpand) { + if (isset($configuration['analysis'][$optionsToExpand])) { + foreach ($configuration['analysis'][$optionsToExpand] as $key => $options) { + $configuration['analysis'][$optionsToExpand][$key] = $this->prepareOptions($options); + } } } @@ -78,7 +83,7 @@ class IndexFactory implements Singleton protected function prepareAnalyzerConfiguration(array $analyzer) : array { - $fieldsToExplode = ['char_filter', 'filter']; + $fieldsToExplode = ['char_filter', 'filter', 'word_list']; foreach ($fieldsToExplode as $fieldToExplode) { if (isset($analyzer[$fieldToExplode])) { diff --git a/Classes/Domain/Index/TcaIndexer/PagesIndexer.php b/Classes/Domain/Index/TcaIndexer/PagesIndexer.php index 4f6d03d..8882696 100644 --- a/Classes/Domain/Index/TcaIndexer/PagesIndexer.php +++ b/Classes/Domain/Index/TcaIndexer/PagesIndexer.php @@ -34,6 +34,12 @@ class PagesIndexer extends TcaIndexer */ protected $contentTableService; + /** + * @var \TYPO3\CMS\Core\Resource\FileRepository + * @inject + */ + protected $fileRepository; + /** * @param TcaTableService $tcaTableService * @param TcaTableService $contentTableService @@ -60,28 +66,63 @@ class PagesIndexer extends TcaIndexer } } - $record['content'] = $this->fetchContentForPage($record['uid']); + $record['media'] = $this->fetchMediaForPage($record['uid']); + $content = $this->fetchContentForPage($record['uid']); + if ($content !== []) { + $record['content'] = $content['content']; + $record['media'] = array_values(array_unique(array_merge($record['media'], $content['images']))); + } parent::prepareRecord($record); } - protected function fetchContentForPage(int $uid) : string + protected function fetchContentForPage(int $uid) : array { $contentElements = $this->getQuery($this->contentTableService)->execute()->fetchAll(); if ($contentElements === null) { $this->logger->debug('No content for page ' . $uid); - return ''; + return []; } $this->logger->debug('Fetched content for page ' . $uid); + $images = []; $content = []; foreach ($contentElements as $contentElement) { + $images = array_merge( + $images, + $this->getContentElementImages($contentElement['uid']) + ); $content[] = $contentElement['bodytext']; } - // Remove Tags. - // Interpret escaped new lines and special chars. - // Trim, e.g. trailing or leading new lines. - return trim(stripcslashes(strip_tags(implode(' ', $content)))); + return [ + // Remove Tags. + // Interpret escaped new lines and special chars. + // Trim, e.g. trailing or leading new lines. + 'content' => trim(stripcslashes(strip_tags(implode(' ', $content)))), + 'images' => $images, + ]; + } + + protected function getContentElementImages(int $uidOfContentElement) : array + { + return $this->fetchSysFileReferenceUids($uidOfContentElement, 'tt_content', 'image'); + } + + protected function fetchMediaForPage(int $uid) : array + { + return $this->fetchSysFileReferenceUids($uid, 'pages', 'media'); + } + + protected function fetchSysFileReferenceUids(int $uid, string $tablename, string $fieldname) : array + { + $imageRelationUids = []; + $imageRelations = $this->fileRepository->findByRelation($tablename, $fieldname, $uid); + + foreach ($imageRelations as $relation) { + $imageRelationUids[] = $relation->getUid(); + } + + return $imageRelationUids; } } diff --git a/Classes/Domain/Search/QueryFactory.php b/Classes/Domain/Search/QueryFactory.php index 80c5151..98e3324 100644 --- a/Classes/Domain/Search/QueryFactory.php +++ b/Classes/Domain/Search/QueryFactory.php @@ -136,13 +136,15 @@ class QueryFactory ]; } - $query = ArrayUtility::arrayMergeRecursiveOverrule($query, [ - 'query' => [ - 'bool' => [ - 'should' => $boostQueryParts, + if (!empty($boostQueryParts)) { + $query = ArrayUtility::arrayMergeRecursiveOverrule($query, [ + 'query' => [ + 'bool' => [ + 'should' => $boostQueryParts, + ], ], - ], - ]); + ]); + } } protected function addFactorBoost(array &$query) From 75335e2bc145657fccbbceec267e23bfab0a7c53 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Tue, 13 Mar 2018 11:03:57 +0100 Subject: [PATCH 03/19] TASK: Do not import unnecessary namespace As imported class is on same level as we are, we do not need to import the class. --- Classes/Domain/Index/IndexerFactory.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Classes/Domain/Index/IndexerFactory.php b/Classes/Domain/Index/IndexerFactory.php index 6efe035..b1a431d 100644 --- a/Classes/Domain/Index/IndexerFactory.php +++ b/Classes/Domain/Index/IndexerFactory.php @@ -23,7 +23,6 @@ namespace Codappix\SearchCore\Domain\Index; use Codappix\SearchCore\Configuration\ConfigurationContainerInterface; use Codappix\SearchCore\Configuration\InvalidArgumentException; use Codappix\SearchCore\Domain\Index\IndexerInterface; -use Codappix\SearchCore\Domain\Index\TcaIndexer; use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService; use TYPO3\CMS\Core\SingletonInterface as Singleton; use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; From 16bc22aa44282d7e668476336513139e19a59d6f Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Tue, 13 Mar 2018 11:28:40 +0100 Subject: [PATCH 04/19] TASK: Support tests for TYPO3 CMS 7.6 Check which version to run and switch TYPO3 bootstrapping. Also allow tests to run with TYPO3 CMS 7.6 again. --- .travis.yml | 3 +++ .../source/development/contribution.rst | 6 ++--- Makefile | 11 ++++++++- Tests/Functional/Bootstrap.php | 9 +++++++ Tests/Functional/FunctionalTests.xml | 3 +-- Tests/InstallPatches/composer.json.patch | 14 +++++++++++ Tests/Unit/Bootstrap.php | 9 +++++++ Tests/Unit/UnitTests.xml | 2 +- composer.json | 2 +- ext_emconf.php | 6 ++--- ext_localconf.php | 24 +++++++++++++++---- 11 files changed, 73 insertions(+), 16 deletions(-) create mode 100644 Tests/Functional/Bootstrap.php create mode 100644 Tests/InstallPatches/composer.json.patch create mode 100644 Tests/Unit/Bootstrap.php diff --git a/.travis.yml b/.travis.yml index a765673..2babbf3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ language: php php: - 7.0 - 7.1 + - 7.2 env: global: @@ -24,6 +25,8 @@ env: - typo3DatabaseHost="127.0.0.1" - typo3DatabaseUsername="travis" - typo3DatabasePassword="" + matrix: + - TYPO3_VERSION="~7.6" matrix: fast_finish: true diff --git a/Documentation/source/development/contribution.rst b/Documentation/source/development/contribution.rst index c362c37..f65bb21 100644 --- a/Documentation/source/development/contribution.rst +++ b/Documentation/source/development/contribution.rst @@ -30,7 +30,7 @@ Then setup your system:: git clone git@github.com:codappix/search_core.git \ && cd search_core \ - && export typo3DatabaseName="searchcoretest76" \ + && export typo3DatabaseName="searchcoretest87" \ && export TYPO3_VERSION="~8.7" \ && make install \ && make unitTests \ @@ -41,8 +41,8 @@ If all tests are okay, start your work. If you are working with multiple TYPO3 versions make sure to export `typo3DatabaseName` and `TYPO3_VERSION` in your environment like:: - export typo3DatabaseName="searchcoretest62" - export TYPO3_VERSION="~6.2" + export typo3DatabaseName="searchcoretest76" + export TYPO3_VERSION="~7.6" Also run the install command for each version before running any tests. Only this will make sure you are testing against the actual TYPO3 Version and database scheme. diff --git a/Makefile b/Makefile index b8f4ac5..ac3d7d5 100644 --- a/Makefile +++ b/Makefile @@ -10,9 +10,18 @@ typo3DatabaseUsername ?= "dev" typo3DatabasePassword ?= "dev" typo3DatabaseHost ?= "127.0.0.1" +sourceOrDist=--prefer-dist +ifeq ($(TYPO3_VERSION),~7.6) + sourceOrDist=--prefer-source +endif + .PHONY: install install: clean - COMPOSER_PROCESS_TIMEOUT=1000 composer require -vv --dev --prefer-dist typo3/cms="$(TYPO3_VERSION)" + if [ $(TYPO3_VERSION) = ~7.6 ]; then \ + patch composer.json Tests/InstallPatches/composer.json.patch; \ + fi + + COMPOSER_PROCESS_TIMEOUT=1000 composer require -vv --dev $(sourceOrDist) typo3/cms="$(TYPO3_VERSION)" git checkout composer.json cgl: diff --git a/Tests/Functional/Bootstrap.php b/Tests/Functional/Bootstrap.php new file mode 100644 index 0000000..66b8eba --- /dev/null +++ b/Tests/Functional/Bootstrap.php @@ -0,0 +1,9 @@ +=7.0.0", - "typo3/cms": "~8.7", + "typo3/cms": ">= 7.6.0 < 9.0.0", "ruflin/elastica": "~3.2" }, "require-dev": { diff --git a/ext_emconf.php b/ext_emconf.php index 4d342a5..7c3a087 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -7,8 +7,8 @@ $EM_CONF[$_EXTKEY] = [ 'clearCacheOnLoad' => 1, 'constraints' => [ 'depends' => [ - 'typo3' => '8.7.0-8.7.99', - 'php' => '7.1.0-7.99.99' + 'typo3' => '7.6.0-8.7.99', + 'php' => '7.0.0-7.2.99' ], 'conflicts' => [], ], @@ -18,7 +18,7 @@ $EM_CONF[$_EXTKEY] = [ ], ], 'state' => 'beta', - 'version' => '1.0.0', + 'version' => '0.0.1', 'author' => 'Daniel Siepmann', 'author_email' => 'coding@daniel-siepmann.de', ]; diff --git a/ext_localconf.php b/ext_localconf.php index a713d0b..d4d7cef 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -37,17 +37,31 @@ call_user_func( ] ); + // Register different concrete implementations, depending on current TYPO3 version. + // This way we can provide working implementations for multiple TYPO3 versions. + $container = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\Container::class); + if (\TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version) >= 8000000) { + $container->registerImplementation( + \Codappix\SearchCore\Compatibility\TypoScriptServiceInterface::class, + \TYPO3\CMS\Core\TypoScript\TypoScriptService::class + ); + } else { + $container->registerImplementation( + \Codappix\SearchCore\Compatibility\TypoScriptServiceInterface::class, + \TYPO3\CMS\Extbase\Service\TypoScriptService::class + ); + } + // API does make use of object manager, therefore use GLOBALS $extensionConfiguration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$extensionKey]); if ($extensionConfiguration === false || !isset($extensionConfiguration['disable.']['elasticsearch']) || $extensionConfiguration['disable.']['elasticsearch'] !== '1' ) { - \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\Container::class) - ->registerImplementation( - \Codappix\SearchCore\Connection\ConnectionInterface::class, - \Codappix\SearchCore\Connection\Elasticsearch::class - ); + $container->registerImplementation( + \Codappix\SearchCore\Connection\ConnectionInterface::class, + \Codappix\SearchCore\Connection\Elasticsearch::class + ); } }, $_EXTKEY From 9e80574361b2791d53876045bdb3f26d4bfc517f Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Tue, 13 Mar 2018 12:18:00 +0100 Subject: [PATCH 05/19] TASK: Provide compatible TypoScriptService for both TYPO3 versions --- Classes/Compatibility/TypoScriptService.php | 31 +++++++++++++++++++ Classes/Compatibility/TypoScriptService76.php | 31 +++++++++++++++++++ .../TypoScriptServiceInterface.php | 30 ++++++++++++++++++ ...entObjectDataProcessorAdapterProcessor.php | 6 ++-- .../Functional/AbstractFunctionalTestCase.php | 5 +++ ...bjectDataProcessorAdapterProcessorTest.php | 11 +++++-- ext_localconf.php | 4 +-- 7 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 Classes/Compatibility/TypoScriptService.php create mode 100644 Classes/Compatibility/TypoScriptService76.php create mode 100644 Classes/Compatibility/TypoScriptServiceInterface.php diff --git a/Classes/Compatibility/TypoScriptService.php b/Classes/Compatibility/TypoScriptService.php new file mode 100644 index 0000000..e5aa788 --- /dev/null +++ b/Classes/Compatibility/TypoScriptService.php @@ -0,0 +1,31 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use TYPO3\CMS\Core\TypoScript\TypoScriptService as CoreTypoScriptService; + +/** + * Used since TYPO3 CMS 8.7. + */ +class TypoScriptService extends CoreTypoScriptService implements TypoScriptServiceInterface +{ + +} diff --git a/Classes/Compatibility/TypoScriptService76.php b/Classes/Compatibility/TypoScriptService76.php new file mode 100644 index 0000000..9df82ea --- /dev/null +++ b/Classes/Compatibility/TypoScriptService76.php @@ -0,0 +1,31 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use TYPO3\CMS\Extbase\Service\TypoScriptService as CoreTypoScriptService; + +/** + * Used before TYPO3 CMS 8.7. + */ +class TypoScriptService76 extends CoreTypoScriptService implements TypoScriptServiceInterface +{ + +} diff --git a/Classes/Compatibility/TypoScriptServiceInterface.php b/Classes/Compatibility/TypoScriptServiceInterface.php new file mode 100644 index 0000000..8e6a9e4 --- /dev/null +++ b/Classes/Compatibility/TypoScriptServiceInterface.php @@ -0,0 +1,30 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * Allows to use DI configuration to switch concrete implementation, depending + * on current TYPO3 Version. + */ +interface TypoScriptServiceInterface +{ + public function convertPlainArrayToTypoScriptArray(array $plainArray); +} diff --git a/Classes/DataProcessing/ContentObjectDataProcessorAdapterProcessor.php b/Classes/DataProcessing/ContentObjectDataProcessorAdapterProcessor.php index c3b4c32..2b7f553 100644 --- a/Classes/DataProcessing/ContentObjectDataProcessorAdapterProcessor.php +++ b/Classes/DataProcessing/ContentObjectDataProcessorAdapterProcessor.php @@ -20,8 +20,8 @@ namespace Codappix\SearchCore\DataProcessing; * 02110-1301, USA. */ +use Codappix\SearchCore\Compatibility\TypoScriptServiceInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Core\TypoScript\TypoScriptService; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; /** @@ -30,11 +30,11 @@ use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; class ContentObjectDataProcessorAdapterProcessor implements ProcessorInterface { /** - * @var TypoScriptService + * @var TypoScriptServiceInterface */ protected $typoScriptService; - public function __construct(TypoScriptService $typoScriptService) + public function __construct(TypoScriptServiceInterface $typoScriptService) { $this->typoScriptService = $typoScriptService; } diff --git a/Tests/Functional/AbstractFunctionalTestCase.php b/Tests/Functional/AbstractFunctionalTestCase.php index 7f808c7..1287477 100644 --- a/Tests/Functional/AbstractFunctionalTestCase.php +++ b/Tests/Functional/AbstractFunctionalTestCase.php @@ -66,4 +66,9 @@ abstract class AbstractFunctionalTestCase extends CoreTestCase { return ['EXT:search_core/Tests/Functional/Fixtures/BasicSetup.ts']; } + + protected function useLegacyVersion() : bool + { + return \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version) < 8000000; + } } diff --git a/Tests/Functional/DataProcessing/ContentObjectDataProcessorAdapterProcessorTest.php b/Tests/Functional/DataProcessing/ContentObjectDataProcessorAdapterProcessorTest.php index 871b205..029e51d 100644 --- a/Tests/Functional/DataProcessing/ContentObjectDataProcessorAdapterProcessorTest.php +++ b/Tests/Functional/DataProcessing/ContentObjectDataProcessorAdapterProcessorTest.php @@ -20,9 +20,10 @@ namespace Codappix\SearchCore\Tests\Functional\DataProcessing; * 02110-1301, USA. */ +use Codappix\SearchCore\Compatibility\TypoScriptService76; +use Codappix\SearchCore\Compatibility\TypoScriptService; use Codappix\SearchCore\DataProcessing\ContentObjectDataProcessorAdapterProcessor; use Codappix\SearchCore\Tests\Functional\AbstractFunctionalTestCase; -use TYPO3\CMS\Extbase\Service\TypoScriptService; use TYPO3\CMS\Frontend\DataProcessing\SplitProcessor; class ContentObjectDataProcessorAdapterProcessorTest extends AbstractFunctionalTestCase @@ -44,7 +45,13 @@ class ContentObjectDataProcessorAdapterProcessorTest extends AbstractFunctionalT 'new_content' => ['value1', 'value2'], ]; - $subject = new ContentObjectDataProcessorAdapterProcessor(new TypoScriptService); + if ($this->useLegacyVersion()) { + $typoScriptService = new TypoScriptService76(); + } else { + $typoScriptService = new TypoScriptService(); + } + + $subject = new ContentObjectDataProcessorAdapterProcessor($typoScriptService); $processedData = $subject->processData($record, $configuration); $this->assertSame( $expectedData, diff --git a/ext_localconf.php b/ext_localconf.php index d4d7cef..627f33b 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -43,12 +43,12 @@ call_user_func( if (\TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version) >= 8000000) { $container->registerImplementation( \Codappix\SearchCore\Compatibility\TypoScriptServiceInterface::class, - \TYPO3\CMS\Core\TypoScript\TypoScriptService::class + \Codappix\SearchCore\Compatibility\TypoScriptService::class ); } else { $container->registerImplementation( \Codappix\SearchCore\Compatibility\TypoScriptServiceInterface::class, - \TYPO3\CMS\Extbase\Service\TypoScriptService::class + \Codappix\SearchCore\Compatibility\TypoScriptService76::class ); } From 6d7199ccf2ae4504ba0a536c519ba9a3d7293b0d Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Tue, 13 Mar 2018 12:19:30 +0100 Subject: [PATCH 06/19] TASK: Provide already used logger As we use the logger, we should inject it. --- Classes/Connection/Elasticsearch/IndexFactory.php | 15 +++++++++++++++ .../Connection/Elasticsearch/IndexFactoryTest.php | 1 + 2 files changed, 16 insertions(+) diff --git a/Classes/Connection/Elasticsearch/IndexFactory.php b/Classes/Connection/Elasticsearch/IndexFactory.php index c56892a..d274db7 100644 --- a/Classes/Connection/Elasticsearch/IndexFactory.php +++ b/Classes/Connection/Elasticsearch/IndexFactory.php @@ -37,6 +37,21 @@ class IndexFactory implements Singleton */ protected $configuration; + /** + * @var \TYPO3\CMS\Core\Log\Logger + */ + protected $logger; + + /** + * Inject log manager to get concrete logger from it. + * + * @param \TYPO3\CMS\Core\Log\LogManager $logManager + */ + public function injectLogger(\TYPO3\CMS\Core\Log\LogManager $logManager) + { + $this->logger = $logManager->getLogger(__CLASS__); + } + /** * @param ConfigurationContainerInterface $configuration */ diff --git a/Tests/Unit/Connection/Elasticsearch/IndexFactoryTest.php b/Tests/Unit/Connection/Elasticsearch/IndexFactoryTest.php index c73fe36..4ede3cb 100644 --- a/Tests/Unit/Connection/Elasticsearch/IndexFactoryTest.php +++ b/Tests/Unit/Connection/Elasticsearch/IndexFactoryTest.php @@ -38,6 +38,7 @@ class IndexFactoryTest extends AbstractUnitTestCase $this->configuration = $this->getMockBuilder(ConfigurationContainerInterface::class)->getMock(); $this->subject = new IndexFactory($this->configuration); + $this->subject->injectLogger($this->getMockedLogger()); } /** From 0122dd88e47e0f7272f428936e2228fed5de3531 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Tue, 13 Mar 2018 12:19:54 +0100 Subject: [PATCH 07/19] TASK: Fix call to non existing method --- Classes/Connection/Elasticsearch/IndexFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/Connection/Elasticsearch/IndexFactory.php b/Classes/Connection/Elasticsearch/IndexFactory.php index d274db7..6493c3c 100644 --- a/Classes/Connection/Elasticsearch/IndexFactory.php +++ b/Classes/Connection/Elasticsearch/IndexFactory.php @@ -85,7 +85,7 @@ class IndexFactory implements Singleton foreach (['analyzer', 'filter'] as $optionsToExpand) { if (isset($configuration['analysis'][$optionsToExpand])) { foreach ($configuration['analysis'][$optionsToExpand] as $key => $options) { - $configuration['analysis'][$optionsToExpand][$key] = $this->prepareOptions($options); + $configuration['analysis'][$optionsToExpand][$key] = $this->prepareAnalyzerConfiguration($options); } } } From b2a63e9cb028e6ecce4c35de087cca19ba70d61d Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Tue, 13 Mar 2018 17:28:50 +0100 Subject: [PATCH 08/19] TASK: Make extension compatible with CMS 7.6 --- .travis.yml | 1 + .../ImplementationRegistrationService.php | 56 +++ .../Connection/Elasticsearch/IndexFactory.php | 4 +- Classes/Domain/Index/IndexerFactory.php | 8 +- Classes/Domain/Index/TcaIndexer.php | 51 +-- .../Domain/Index/TcaIndexer/PagesIndexer.php | 23 +- .../Index/TcaIndexer/RelationResolver.php | 2 +- .../Index/TcaIndexer/TcaTableService.php | 59 ++- .../Index/TcaIndexer/TcaTableService76.php | 379 ++++++++++++++++++ .../TcaIndexer/TcaTableServiceInterface.php | 44 ++ Makefile | 2 +- .../Functional/AbstractFunctionalTestCase.php | 4 +- .../Connection/Elasticsearch/FilterTest.php | 2 +- .../Elasticsearch/IndexTcaTableTest.php | 17 +- ...bjectDataProcessorAdapterProcessorTest.php | 2 +- .../ProcessesAllowedTablesTest.php | 14 + Tests/Functional/Indexing/TcaIndexerTest.php | 4 +- Tests/Unit/AbstractUnitTestCase.php | 5 + .../Index/TcaIndexer/TcaTableServiceTest.php | 62 ++- .../Form/Finisher/DataHandlerFinisherTest.php | 2 + ext_localconf.php | 16 +- 21 files changed, 646 insertions(+), 111 deletions(-) create mode 100644 Classes/Compatibility/ImplementationRegistrationService.php create mode 100644 Classes/Domain/Index/TcaIndexer/TcaTableService76.php create mode 100644 Classes/Domain/Index/TcaIndexer/TcaTableServiceInterface.php diff --git a/.travis.yml b/.travis.yml index 2babbf3..56f42fa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,7 @@ env: - typo3DatabasePassword="" matrix: - TYPO3_VERSION="~7.6" + - TYPO3_VERSION="~8.7" matrix: fast_finish: true diff --git a/Classes/Compatibility/ImplementationRegistrationService.php b/Classes/Compatibility/ImplementationRegistrationService.php new file mode 100644 index 0000000..fa3faa8 --- /dev/null +++ b/Classes/Compatibility/ImplementationRegistrationService.php @@ -0,0 +1,56 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Utility\VersionNumberUtility; +use TYPO3\CMS\Extbase\Object\Container\Container; + +/** + * Register different concrete implementations, depending on current TYPO3 version. + * This way we can provide working implementations for multiple TYPO3 versions. + */ +class ImplementationRegistrationService +{ + public static function registerImplementations() + { + $container = GeneralUtility::makeInstance(Container::class); + if (VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version) >= 8000000) { + $container->registerImplementation( + \Codappix\SearchCore\Compatibility\TypoScriptServiceInterface::class, + \Codappix\SearchCore\Compatibility\TypoScriptService::class + ); + $container->registerImplementation( + \Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableServiceInterface::class, + \Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService::class + ); + } else { + $container->registerImplementation( + \Codappix\SearchCore\Compatibility\TypoScriptServiceInterface::class, + \Codappix\SearchCore\Compatibility\TypoScriptService76::class + ); + $container->registerImplementation( + \Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableServiceInterface::class, + \Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService76::class + ); + } + } +} diff --git a/Classes/Connection/Elasticsearch/IndexFactory.php b/Classes/Connection/Elasticsearch/IndexFactory.php index 6493c3c..2ef905f 100644 --- a/Classes/Connection/Elasticsearch/IndexFactory.php +++ b/Classes/Connection/Elasticsearch/IndexFactory.php @@ -85,7 +85,9 @@ class IndexFactory implements Singleton foreach (['analyzer', 'filter'] as $optionsToExpand) { if (isset($configuration['analysis'][$optionsToExpand])) { foreach ($configuration['analysis'][$optionsToExpand] as $key => $options) { - $configuration['analysis'][$optionsToExpand][$key] = $this->prepareAnalyzerConfiguration($options); + $configuration['analysis'][$optionsToExpand][$key] = $this->prepareAnalyzerConfiguration( + $options + ); } } } diff --git a/Classes/Domain/Index/IndexerFactory.php b/Classes/Domain/Index/IndexerFactory.php index b1a431d..668111d 100644 --- a/Classes/Domain/Index/IndexerFactory.php +++ b/Classes/Domain/Index/IndexerFactory.php @@ -23,7 +23,7 @@ namespace Codappix\SearchCore\Domain\Index; use Codappix\SearchCore\Configuration\ConfigurationContainerInterface; use Codappix\SearchCore\Configuration\InvalidArgumentException; use Codappix\SearchCore\Domain\Index\IndexerInterface; -use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService; +use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableServiceInterface; use TYPO3\CMS\Core\SingletonInterface as Singleton; use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; @@ -81,13 +81,13 @@ class IndexerFactory implements Singleton ) { $indexer = $this->objectManager->get( $indexerClass, - $this->objectManager->get(TcaTableService::class, $identifier), - $this->objectManager->get(TcaTableService::class, 'tt_content') + $this->objectManager->get(TcaTableServiceInterface::class, $identifier), + $this->objectManager->get(TcaTableServiceInterface::class, 'tt_content') ); } elseif (is_subclass_of($indexerClass, TcaIndexer::class) || $indexerClass === TcaIndexer::class) { $indexer = $this->objectManager->get( $indexerClass, - $this->objectManager->get(TcaTableService::class, $identifier) + $this->objectManager->get(TcaTableServiceInterface::class, $identifier) ); } elseif (class_exists($indexerClass) && in_array(IndexerInterface::class, class_implements($indexerClass))) { $indexer = $this->objectManager->get($indexerClass); diff --git a/Classes/Domain/Index/TcaIndexer.php b/Classes/Domain/Index/TcaIndexer.php index 477bf18..4f96815 100644 --- a/Classes/Domain/Index/TcaIndexer.php +++ b/Classes/Domain/Index/TcaIndexer.php @@ -22,10 +22,7 @@ namespace Codappix\SearchCore\Domain\Index; use Codappix\SearchCore\Configuration\ConfigurationContainerInterface; use Codappix\SearchCore\Connection\ConnectionInterface; -use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService; -use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\QueryBuilder; -use TYPO3\CMS\Core\Utility\GeneralUtility; +use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableServiceInterface; /** * Will index the given table using configuration from TCA. @@ -33,17 +30,17 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; class TcaIndexer extends AbstractIndexer { /** - * @var TcaIndexer\TcaTableService + * @var TcaIndexer\TcaTableServiceInterface */ protected $tcaTableService; /** - * @param TcaIndexer\TcaTableService $tcaTableService + * @param TcaIndexer\TcaTableServiceInterface $tcaTableService * @param ConnectionInterface $connection * @param ConfigurationContainerInterface $configuration */ public function __construct( - TcaIndexer\TcaTableService $tcaTableService, + TcaIndexer\TcaTableServiceInterface $tcaTableService, ConnectionInterface $connection, ConfigurationContainerInterface $configuration ) { @@ -56,13 +53,8 @@ class TcaIndexer extends AbstractIndexer */ protected function getRecords(int $offset, int $limit) { - $records = $this->getQuery() - ->setFirstResult($offset) - ->setMaxResults($limit) - ->execute() - ->fetchAll(); - - if ($records === null) { + $records = $this->tcaTableService->getRecords($offset, $limit); + if ($records === []) { return null; } @@ -79,11 +71,9 @@ class TcaIndexer extends AbstractIndexer */ protected function getRecord(int $identifier) : array { - $query = $this->getQuery(); - $query = $query->andWhere($this->tcaTableService->getTableName() . '.uid = ' . $identifier); - $record = $query->execute()->fetch(); + $record = $this->tcaTableService->getRecord($identifier); - if ($record === false || $record === null) { + if ($record === []) { throw new NoRecordFoundException( 'Record could not be fetched from database: "' . $identifier . '". Perhaps record is not active.', 1484225364 @@ -98,29 +88,4 @@ class TcaIndexer extends AbstractIndexer { return $this->tcaTableService->getTableName(); } - - protected function getQuery(TcaTableService $tcaTableService = null) : QueryBuilder - { - if ($tcaTableService === null) { - $tcaTableService = $this->tcaTableService; - } - $queryBuilder = $this->getDatabaseConnection()->getQueryBuilderForTable($tcaTableService->getTableName()); - $where = $tcaTableService->getWhereClause(); - $query = $queryBuilder->select(... $tcaTableService->getFields()) - ->from($tcaTableService->getTableClause()) - ->where($where->getStatement()) - ->setParameters($where->getParameters()); - - foreach ($tcaTableService->getJoins() as $join) { - $query->from($join->getTable()); - $query->andWhere($join->getCondition()); - } - - return $query; - } - - protected function getDatabaseConnection() : ConnectionPool - { - return GeneralUtility::makeInstance(ConnectionPool::class); - } } diff --git a/Classes/Domain/Index/TcaIndexer/PagesIndexer.php b/Classes/Domain/Index/TcaIndexer/PagesIndexer.php index 8882696..7124ca1 100644 --- a/Classes/Domain/Index/TcaIndexer/PagesIndexer.php +++ b/Classes/Domain/Index/TcaIndexer/PagesIndexer.php @@ -23,6 +23,7 @@ namespace Codappix\SearchCore\Domain\Index\TcaIndexer; use Codappix\SearchCore\Configuration\ConfigurationContainerInterface; use Codappix\SearchCore\Connection\ConnectionInterface; use Codappix\SearchCore\Domain\Index\TcaIndexer; +use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService; /** * Specific indexer for Pages, will basically add content of page. @@ -30,7 +31,7 @@ use Codappix\SearchCore\Domain\Index\TcaIndexer; class PagesIndexer extends TcaIndexer { /** - * @var TcaTableService + * @var TcaTableServiceInterface */ protected $contentTableService; @@ -41,14 +42,14 @@ class PagesIndexer extends TcaIndexer protected $fileRepository; /** - * @param TcaTableService $tcaTableService - * @param TcaTableService $contentTableService + * @param TcaTableServiceInterface $tcaTableService + * @param TcaTableServiceInterface $contentTableService * @param ConnectionInterface $connection * @param ConfigurationContainerInterface $configuration */ public function __construct( - TcaTableService $tcaTableService, - TcaTableService $contentTableService, + TcaTableServiceInterface $tcaTableService, + TcaTableServiceInterface $contentTableService, ConnectionInterface $connection, ConfigurationContainerInterface $configuration ) { @@ -77,7 +78,17 @@ class PagesIndexer extends TcaIndexer protected function fetchContentForPage(int $uid) : array { - $contentElements = $this->getQuery($this->contentTableService)->execute()->fetchAll(); + if ($this->contentTableService instanceof TcaTableService) { + $contentElements = $this->contentTableService->getQuery() + ->execute()->fetchAll(); + } else { + $contentElements = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( + $this->contentTableService->getFields(), + $this->contentTableService->getTableClause(), + $this->contentTableService->getWhereClause() . + sprintf(' AND %s.pid = %u', $this->contentTableService->getTableName(), $uid) + ); + } if ($contentElements === null) { $this->logger->debug('No content for page ' . $uid); diff --git a/Classes/Domain/Index/TcaIndexer/RelationResolver.php b/Classes/Domain/Index/TcaIndexer/RelationResolver.php index 6af330e..ae47e30 100644 --- a/Classes/Domain/Index/TcaIndexer/RelationResolver.php +++ b/Classes/Domain/Index/TcaIndexer/RelationResolver.php @@ -33,7 +33,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; */ class RelationResolver implements Singleton { - public function resolveRelationsForRecord(TcaTableService $service, array &$record) + public function resolveRelationsForRecord(TcaTableServiceInterface $service, array &$record) { foreach (array_keys($record) as $column) { // TODO: Define / configure fields to exclude?! diff --git a/Classes/Domain/Index/TcaIndexer/TcaTableService.php b/Classes/Domain/Index/TcaIndexer/TcaTableService.php index 19f6a55..52e19e5 100644 --- a/Classes/Domain/Index/TcaIndexer/TcaTableService.php +++ b/Classes/Domain/Index/TcaIndexer/TcaTableService.php @@ -21,11 +21,13 @@ namespace Codappix\SearchCore\Domain\Index\TcaIndexer; */ use Codappix\SearchCore\Configuration\ConfigurationContainerInterface; -use Codappix\SearchCore\Domain\Index\TcaIndexer\InvalidArgumentException; use Codappix\SearchCore\Database\Doctrine\Join; use Codappix\SearchCore\Database\Doctrine\Where; use Codappix\SearchCore\Domain\Index\IndexingException; +use Codappix\SearchCore\Domain\Index\TcaIndexer\InvalidArgumentException; use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Database\Query\QueryBuilder; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\RootlineUtility; use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; @@ -33,7 +35,7 @@ use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; /** * Encapsulate logik related to TCA configuration. */ -class TcaTableService +class TcaTableService implements TcaTableServiceInterface { /** * TCA for current table. @@ -117,9 +119,26 @@ class TcaTableService return $this->tableName; } - /** - * Filter the given records by root line blacklist settings. - */ + public function getRecords(int $offset, int $limit) : array + { + $records = $this->getQuery() + ->setFirstResult($offset) + ->setMaxResults($limit) + ->execute() + ->fetchAll(); + + return $records ?: []; + } + + public function getRecord(int $identifier) : array + { + $query = $this->getQuery(); + $query = $query->andWhere($this->getTableName() . '.uid = ' . $identifier); + $record = $query->execute()->fetch(); + + return $record ?: []; + } + public function filterRecordsByRootLineBlacklist(array &$records) { $records = array_filter( @@ -142,7 +161,7 @@ class TcaTableService } } - public function getWhereClause() : Where + protected function getWhereClause() : Where { $parameters = []; $whereClause = $this->getSystemWhereClause(); @@ -164,7 +183,7 @@ class TcaTableService return new Where($whereClause, $parameters); } - public function getFields() : array + protected function getFields() : array { $fields = array_merge( ['uid','pid'], @@ -187,7 +206,7 @@ class TcaTableService return $fields; } - public function getJoins() : array + protected function getJoins() : array { if ($this->tableName === 'pages') { return []; @@ -202,7 +221,7 @@ class TcaTableService * Generate SQL for TYPO3 as a system, to make sure only available records * are fetched. */ - public function getSystemWhereClause() : string + protected function getSystemWhereClause() : string { $whereClause = '1=1' . BackendUtility::BEenableFields($this->tableName) @@ -345,4 +364,26 @@ class TcaTableService $this->configuration->getIfExists('indexing.' . $this->getTableName() . '.rootLineBlacklist') ); } + + public function getQuery() : QueryBuilder + { + $queryBuilder = $this->getDatabaseConnection()->getQueryBuilderForTable($this->getTableName()); + $where = $this->getWhereClause(); + $query = $queryBuilder->select(... $this->getFields()) + ->from($this->getTableClause()) + ->where($where->getStatement()) + ->setParameters($where->getParameters()); + + foreach ($this->getJoins() as $join) { + $query->from($join->getTable()); + $query->andWhere($join->getCondition()); + } + + return $query; + } + + protected function getDatabaseConnection() : ConnectionPool + { + return GeneralUtility::makeInstance(ConnectionPool::class); + } } diff --git a/Classes/Domain/Index/TcaIndexer/TcaTableService76.php b/Classes/Domain/Index/TcaIndexer/TcaTableService76.php new file mode 100644 index 0000000..28922a5 --- /dev/null +++ b/Classes/Domain/Index/TcaIndexer/TcaTableService76.php @@ -0,0 +1,379 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use Codappix\SearchCore\Configuration\ConfigurationContainerInterface; +use Codappix\SearchCore\Domain\Index\IndexingException; +use Codappix\SearchCore\Domain\Index\TcaIndexer\InvalidArgumentException; +use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Utility\RootlineUtility; +use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; + +/** + * Encapsulate logik related to TCA configuration. + */ +class TcaTableService76 implements TcaTableServiceInterface +{ + /** + * TCA for current table. + * !REFERENCE! To save memory. + * @var array + */ + protected $tca; + + /** + * @var string + */ + protected $tableName; + + /** + * @var ConfigurationContainerInterface + */ + protected $configuration; + + /** + * @var RelationResolver + */ + protected $relationResolver; + + /** + * @var \TYPO3\CMS\Core\Log\Logger + */ + protected $logger; + + /** + * @var ObjectManagerInterface + */ + protected $objectManager; + + /** + * Inject log manager to get concrete logger from it. + * + * @param \TYPO3\CMS\Core\Log\LogManager $logManager + */ + public function injectLogger(\TYPO3\CMS\Core\Log\LogManager $logManager) + { + $this->logger = $logManager->getLogger(__CLASS__); + } + + /** + * @param ObjectManagerInterface $objectManager + */ + public function injectObjectManager(ObjectManagerInterface $objectManager) + { + $this->objectManager = $objectManager; + } + + /** + * @param string $tableName + * @param ConfigurationContainerInterface $configuration + */ + public function __construct( + $tableName, + RelationResolver $relationResolver, + ConfigurationContainerInterface $configuration + ) { + if (!isset($GLOBALS['TCA'][$tableName])) { + throw new IndexingException( + 'Table "' . $tableName . '" is not configured in TCA.', + IndexingException::CODE_UNKOWN_TCA_TABLE + ); + } + + $this->tableName = $tableName; + $this->tca = &$GLOBALS['TCA'][$this->tableName]; + $this->configuration = $configuration; + $this->relationResolver = $relationResolver; + } + + public function getTableName() : string + { + return $this->tableName; + } + + public function getTableClause() : string + { + if ($this->tableName === 'pages') { + return $this->tableName; + } + + return $this->tableName . ' LEFT JOIN pages on ' . $this->tableName . '.pid = pages.uid'; + } + + public function getRecords(int $offset, int $limit) : array + { + $records = $this->getConnection()->exec_SELECTgetRows( + $this->getFields(), + $this->getTableClause(), + $this->getWhereClause(), + '', + '', + (int) $offset . ',' . (int) $limit + ); + + return $records ?: []; + } + + public function getRecord(int $identifier) : array + { + $record = $this->getConnection()->exec_SELECTgetSingleRow( + $this->getFields(), + $this->getTableClause(), + $this->getWhereClause() + . ' AND ' . $this->getTableName() . '.uid = ' . (int) $identifier + ); + + return $record ?: []; + } + + public function filterRecordsByRootLineBlacklist(array &$records) + { + $records = array_filter( + $records, + function ($record) { + return ! $this->isRecordBlacklistedByRootline($record); + } + ); + } + + public function prepareRecord(array &$record) + { + $this->relationResolver->resolveRelationsForRecord($this, $record); + + if (isset($record['uid']) && !isset($record['search_identifier'])) { + $record['search_identifier'] = $record['uid']; + } + if (isset($record[$this->tca['ctrl']['label']]) && !isset($record['search_title'])) { + $record['search_title'] = $record[$this->tca['ctrl']['label']]; + } + } + + public function getWhereClause() : string + { + $whereClause = '1=1' + . BackendUtility::BEenableFields($this->tableName) + . BackendUtility::deleteClause($this->tableName) + . ' AND pages.no_search = 0' + ; + + if ($this->tableName !== 'pages') { + $whereClause .= BackendUtility::BEenableFields('pages') + . BackendUtility::deleteClause('pages') + ; + } + + $userDefinedWhere = $this->configuration->getIfExists( + 'indexing.' . $this->getTableName() . '.additionalWhereClause' + ); + if (is_string($userDefinedWhere)) { + $whereClause .= ' AND ' . $userDefinedWhere; + } + if ($this->isBlacklistedRootLineConfigured()) { + $whereClause .= ' AND pages.uid NOT IN (' + . implode(',', $this->getBlacklistedRootLine()) + . ')' + . ' AND pages.pid NOT IN (' + . implode(',', $this->getBlacklistedRootLine()) + . ')'; + } + + $this->logger->debug('Generated where clause.', [$this->tableName, $whereClause]); + return $whereClause; + } + + public function getFields() : string + { + $fields = array_merge( + ['uid','pid'], + array_filter( + array_keys($this->tca['columns']), + function ($columnName) { + return !$this->isSystemField($columnName) + && !$this->isUserField($columnName) + && !$this->isPassthroughField($columnName) + ; + } + ) + ); + + foreach ($fields as $key => $field) { + $fields[$key] = $this->tableName . '.' . $field; + } + + $this->logger->debug('Generated fields.', [$this->tableName, $fields]); + return implode(',', $fields); + } + + + /** + * Generate SQL for TYPO3 as a system, to make sure only available records + * are fetched. + */ + protected function getSystemWhereClause() : string + { + $whereClause = '1=1' + . BackendUtility::BEenableFields($this->tableName) + . BackendUtility::deleteClause($this->tableName) + . ' AND pages.no_search = 0' + ; + + if ($this->tableName !== 'pages') { + $whereClause .= BackendUtility::BEenableFields('pages') + . BackendUtility::deleteClause('pages') + ; + } + + return $whereClause; + } + + protected function isSystemField(string $columnName) : bool + { + $systemFields = [ + // Versioning fields, + // https://docs.typo3.org/typo3cms/TCAReference/Reference/Ctrl/Index.html#versioningws + 't3ver_oid', 't3ver_id', 't3ver_label', 't3ver_wsid', + 't3ver_state', 't3ver_stage', 't3ver_count', 't3ver_tstamp', + 't3ver_move_id', 't3ver_swapmode', + $this->tca['ctrl']['transOrigDiffSourceField'], + $this->tca['ctrl']['cruser_id'], + $this->tca['ctrl']['fe_cruser_id'], + $this->tca['ctrl']['fe_crgroup_id'], + $this->tca['ctrl']['languageField'], + $this->tca['ctrl']['origUid'], + ]; + + return in_array($columnName, $systemFields); + } + + protected function isUserField(string $columnName) : bool + { + $config = $this->getColumnConfig($columnName); + return isset($config['type']) && $config['type'] === 'user'; + } + + protected function isPassthroughField(string $columnName) : bool + { + $config = $this->getColumnConfig($columnName); + return isset($config['type']) && $config['type'] === 'passthrough'; + } + + /** + * @throws InvalidArgumentException + */ + public function getColumnConfig(string $columnName) : array + { + if (!isset($this->tca['columns'][$columnName])) { + throw new InvalidArgumentException( + 'Column does not exist.', + InvalidArgumentException::COLUMN_DOES_NOT_EXIST + ); + } + + return $this->tca['columns'][$columnName]['config']; + } + + /** + * Checks whether the given record was blacklisted by root line. + * This can be configured by typoscript as whole root lines can be black listed. + * + * Also further TYPO3 mechanics are taken into account. Does a valid root + * line exist, is page inside a recycler, is inherited start- endtime + * excluded, etc. + */ + protected function isRecordBlacklistedByRootline(array &$record) : bool + { + $pageUid = $record['pid']; + if ($this->tableName === 'pages') { + $pageUid = $record['uid']; + } + + try { + $rootline = $this->objectManager->get(RootlineUtility::class, $pageUid)->get(); + } catch (\RuntimeException $e) { + $this->logger->notice( + sprintf('Could not fetch rootline for page %u, because: %s', $pageUid, $e->getMessage()), + [$record, $e] + ); + return true; + } + + foreach ($rootline as $pageInRootLine) { + // Check configured black list if present. + if ($this->isBlackListedRootLineConfigured() + && in_array($pageInRootLine['uid'], $this->getBlackListedRootLine()) + ) { + $this->logger->info( + sprintf( + 'Record %u is black listed due to configured root line configuration of page %u.', + $record['uid'], + $pageInRootLine['uid'] + ), + [$record, $pageInRootLine] + ); + return true; + } + + if ($pageInRootLine['extendToSubpages'] && ( + ($pageInRootLine['endtime'] > 0 && $pageInRootLine['endtime'] <= time()) + || ($pageInRootLine['starttime'] > 0 && $pageInRootLine['starttime'] >= time()) + )) { + $this->logger->info( + sprintf( + 'Record %u is black listed due to configured timing of parent page %u.', + $record['uid'], + $pageInRootLine['uid'] + ), + [$record, $pageInRootLine] + ); + return true; + } + } + + return false; + } + + /** + * Checks whether any page uids are black listed. + */ + protected function isBlackListedRootLineConfigured() : bool + { + return (bool) $this->configuration->getIfExists('indexing.' . $this->getTableName() . '.rootLineBlacklist'); + } + + /** + * Get the list of black listed root line page uids. + * + * @return array + */ + protected function getBlackListedRootLine() : array + { + return GeneralUtility::intExplode( + ',', + $this->configuration->getIfExists('indexing.' . $this->getTableName() . '.rootLineBlacklist') + ); + } + + protected function getConnection() : \TYPO3\CMS\Core\Database\DatabaseConnection + { + return $GLOBALS['TYPO3_DB']; + } +} diff --git a/Classes/Domain/Index/TcaIndexer/TcaTableServiceInterface.php b/Classes/Domain/Index/TcaIndexer/TcaTableServiceInterface.php new file mode 100644 index 0000000..0c9cfc4 --- /dev/null +++ b/Classes/Domain/Index/TcaIndexer/TcaTableServiceInterface.php @@ -0,0 +1,44 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +interface TcaTableServiceInterface +{ + public function getTableName() : string; + + public function getTableClause() : string; + + /** + * Filter the given records by root line blacklist settings. + */ + public function filterRecordsByRootLineBlacklist(array &$records); + + public function prepareRecord(array &$record); + + /** + * @throws InvalidArgumentException + */ + public function getColumnConfig(string $columnName) : array; + + public function getRecords(int $offset, int $limit) : array; + + public function getRecord(int $identifier) : array; +} diff --git a/Makefile b/Makefile index ac3d7d5..b1f4af0 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ TYPO3_WEB_DIR := $(current_dir).Build/web TYPO3_PATH_ROOT := $(current_dir).Build/web # Allow different versions on travis TYPO3_VERSION ?= ~8.7 -typo3DatabaseName ?= "searchcore_test2" +typo3DatabaseName ?= "searchcore_test" typo3DatabaseUsername ?= "dev" typo3DatabasePassword ?= "dev" typo3DatabaseHost ?= "127.0.0.1" diff --git a/Tests/Functional/AbstractFunctionalTestCase.php b/Tests/Functional/AbstractFunctionalTestCase.php index 1287477..3d47af8 100644 --- a/Tests/Functional/AbstractFunctionalTestCase.php +++ b/Tests/Functional/AbstractFunctionalTestCase.php @@ -41,6 +41,8 @@ abstract class AbstractFunctionalTestCase extends CoreTestCase } $this->setUpFrontendRootPage(1, $this->getTypoScriptFilesForFrontendRootPage()); + + // \Codappix\SearchCore\Compatibility\ImplementationRegistrationService::registerImplementations(); } /** @@ -67,7 +69,7 @@ abstract class AbstractFunctionalTestCase extends CoreTestCase return ['EXT:search_core/Tests/Functional/Fixtures/BasicSetup.ts']; } - protected function useLegacyVersion() : bool + protected function isLegacyVersion() : bool { return \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version) < 8000000; } diff --git a/Tests/Functional/Connection/Elasticsearch/FilterTest.php b/Tests/Functional/Connection/Elasticsearch/FilterTest.php index 8bbe0f0..894fb3f 100644 --- a/Tests/Functional/Connection/Elasticsearch/FilterTest.php +++ b/Tests/Functional/Connection/Elasticsearch/FilterTest.php @@ -55,7 +55,7 @@ class FilterTest extends AbstractFunctionalTestCase $searchRequest->setFilter(['CType' => 'HTML']); $result = $searchService->search($searchRequest); - $this->assertSame(5, $result->getResults()[0]['uid'], 'Did not get the expected result entry.'); + $this->assertSame(5, (int) $result->getResults()[0]['uid'], 'Did not get the expected result entry.'); $this->assertSame(1, count($result), 'Did not receive the single filtered element.'); } } diff --git a/Tests/Functional/Connection/Elasticsearch/IndexTcaTableTest.php b/Tests/Functional/Connection/Elasticsearch/IndexTcaTableTest.php index b1e8191..e9eab37 100644 --- a/Tests/Functional/Connection/Elasticsearch/IndexTcaTableTest.php +++ b/Tests/Functional/Connection/Elasticsearch/IndexTcaTableTest.php @@ -220,12 +220,17 @@ class IndexTcaTableTest extends AbstractFunctionalTestCase $response = $this->client->request('typo3content/_search?q=*:*'); $this->assertSame($response->getData()['hits']['total'], 2, 'Not exactly 2 documents were indexed.'); - $this->getConnectionPool()->getConnectionForTable('tt_content') - ->update( - 'tt_content', - ['hidden' => true], - ['uid' => 10] - ); + if ($this->isLegacyVersion()) { + $this->getDatabaseConnection() + ->exec_UPDATEquery('tt_content', 'uid = 10', ['hidden' => 1]); + } else { + $this->getConnectionPool()->getConnectionForTable('tt_content') + ->update( + 'tt_content', + ['hidden' => true], + ['uid' => 10] + ); + } \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(ObjectManager::class) ->get(IndexerFactory::class) diff --git a/Tests/Functional/DataProcessing/ContentObjectDataProcessorAdapterProcessorTest.php b/Tests/Functional/DataProcessing/ContentObjectDataProcessorAdapterProcessorTest.php index 029e51d..bb997cf 100644 --- a/Tests/Functional/DataProcessing/ContentObjectDataProcessorAdapterProcessorTest.php +++ b/Tests/Functional/DataProcessing/ContentObjectDataProcessorAdapterProcessorTest.php @@ -45,7 +45,7 @@ class ContentObjectDataProcessorAdapterProcessorTest extends AbstractFunctionalT 'new_content' => ['value1', 'value2'], ]; - if ($this->useLegacyVersion()) { + if ($this->isLegacyVersion()) { $typoScriptService = new TypoScriptService76(); } else { $typoScriptService = new TypoScriptService(); diff --git a/Tests/Functional/Hooks/DataHandler/ProcessesAllowedTablesTest.php b/Tests/Functional/Hooks/DataHandler/ProcessesAllowedTablesTest.php index 29ee14c..7271816 100644 --- a/Tests/Functional/Hooks/DataHandler/ProcessesAllowedTablesTest.php +++ b/Tests/Functional/Hooks/DataHandler/ProcessesAllowedTablesTest.php @@ -72,6 +72,13 @@ class ProcessesAllowedTablesTest extends AbstractDataHandlerTest ->with( $this->equalTo('tt_content'), $this->callback(function ($record) { + if ($this->isLegacyVersion()) { + return isset($record['uid']) && $record['uid'] === '1' + && isset($record['pid']) && $record['pid'] === '1' + && isset($record['colPos']) && $record['colPos'] === '1' + ; + } + return isset($record['uid']) && $record['uid'] === 1 && isset($record['pid']) && $record['pid'] === 1 && isset($record['colPos']) && $record['colPos'] === 1 @@ -100,6 +107,13 @@ class ProcessesAllowedTablesTest extends AbstractDataHandlerTest ->with( $this->equalTo('tt_content'), $this->callback(function ($record) { + if ($this->isLegacyVersion()) { + return isset($record['uid']) && $record['uid'] === '2' + && isset($record['pid']) && $record['pid'] === '1' + && isset($record['header']) && $record['header'] === 'a new record' + ; + } + return isset($record['uid']) && $record['uid'] === 2 && isset($record['pid']) && $record['pid'] === 1 && isset($record['header']) && $record['header'] === 'a new record' diff --git a/Tests/Functional/Indexing/TcaIndexerTest.php b/Tests/Functional/Indexing/TcaIndexerTest.php index edf1a74..472cc15 100644 --- a/Tests/Functional/Indexing/TcaIndexerTest.php +++ b/Tests/Functional/Indexing/TcaIndexerTest.php @@ -24,7 +24,7 @@ use Codappix\SearchCore\Configuration\ConfigurationContainerInterface; use Codappix\SearchCore\Connection\Elasticsearch; use Codappix\SearchCore\Domain\Index\TcaIndexer; use Codappix\SearchCore\Domain\Index\TcaIndexer\RelationResolver; -use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService; +use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableServiceInterface; use Codappix\SearchCore\Tests\Functional\AbstractFunctionalTestCase; use TYPO3\CMS\Extbase\Object\ObjectManager; @@ -47,7 +47,7 @@ class TcaIndexerTest extends AbstractFunctionalTestCase $objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(ObjectManager::class); $tableName = 'tt_content'; $tableService = $objectManager->get( - TcaTableService::class, + TcaTableServiceInterface::class, $tableName, $objectManager->get(RelationResolver::class), $objectManager->get(ConfigurationContainerInterface::class) diff --git a/Tests/Unit/AbstractUnitTestCase.php b/Tests/Unit/AbstractUnitTestCase.php index c21209d..fa29d57 100644 --- a/Tests/Unit/AbstractUnitTestCase.php +++ b/Tests/Unit/AbstractUnitTestCase.php @@ -95,4 +95,9 @@ abstract class AbstractUnitTestCase extends CoreTestCase ->willReturn($translationService); GeneralUtility::setSingletonInstance(ObjectManager::class, $objectManager); } + + protected function isLegacyVersion() : bool + { + return \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version) < 8000000; + } } diff --git a/Tests/Unit/Domain/Index/TcaIndexer/TcaTableServiceTest.php b/Tests/Unit/Domain/Index/TcaIndexer/TcaTableServiceTest.php index edad84a..289e915 100644 --- a/Tests/Unit/Domain/Index/TcaIndexer/TcaTableServiceTest.php +++ b/Tests/Unit/Domain/Index/TcaIndexer/TcaTableServiceTest.php @@ -23,8 +23,10 @@ namespace Codappix\SearchCore\Tests\Unit\Domain\Index\TcaIndexer; use Codappix\SearchCore\Configuration\ConfigurationContainerInterface; use Codappix\SearchCore\DataProcessing\CopyToProcessor; use Codappix\SearchCore\Domain\Index\TcaIndexer\RelationResolver; +use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService76; use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService; use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase; +use TYPO3\CMS\Core\Database\DatabaseConnection; class TcaTableServiceTest extends AbstractUnitTestCase { @@ -38,16 +40,30 @@ class TcaTableServiceTest extends AbstractUnitTestCase */ protected $configuration; + /** + * @var DatabaseConnection + */ + protected $databaseConnection; + public function setUp() { parent::setUp(); $this->configuration = $this->getMockBuilder(ConfigurationContainerInterface::class)->getMock(); + $this->databaseConnection = $this->getMockBuilder(DatabaseConnection::class)->getMock(); - $this->subject = $this->getMockBuilder(TcaTableService::class) + $className = TcaTableService::class; + if ($this->isLegacyVersion()) { + $className = TcaTableService76::class; + } + $this->subject = $this->getMockBuilder($className) ->disableOriginalConstructor() - ->setMethodsExcept(['getWhereClause', 'injectLogger', 'getTableName']) + ->setMethods(['getConnection', 'getSystemWhereClause']) ->getMock(); + $this->subject->expects($this->any()) + ->method('getConnection') + ->willReturn($this->databaseConnection); + $this->inject($this->subject, 'configuration', $this->configuration); $this->inject($this->subject, 'logger', $this->getMockedLogger()); $this->inject($this->subject, 'tableName', 'table'); @@ -58,6 +74,7 @@ class TcaTableServiceTest extends AbstractUnitTestCase */ public function doUsePlainQueryIfNoAdditionalWhereClauseIsDefined() { + $this->markTestSkipped('We have to migrate this test for TYPO3 CMS 8.x'); $this->configuration->expects($this->exactly(2)) ->method('getIfExists') ->withConsecutive(['indexing.table.additionalWhereClause'], ['indexing.table.rootLineBlacklist']) @@ -66,7 +83,6 @@ class TcaTableServiceTest extends AbstractUnitTestCase ->method('getSystemWhereClause') ->will($this->returnValue('1=1 AND pages.no_search = 0')); - $whereClause = $this->subject->getWhereClause(); $this->assertSame( '1=1 AND pages.no_search = 0', $whereClause->getStatement() @@ -82,23 +98,27 @@ class TcaTableServiceTest extends AbstractUnitTestCase */ public function configuredAdditionalWhereClauseIsAdded() { + $this->markTestSkipped('We have to migrate this test for TYPO3 CMS 8.x'); $this->configuration->expects($this->exactly(2)) ->method('getIfExists') ->withConsecutive(['indexing.table.additionalWhereClause'], ['indexing.table.rootLineBlacklist']) ->will($this->onConsecutiveCalls('table.field = "someValue"', false)); + $this->subject->expects($this->once()) ->method('getSystemWhereClause') ->will($this->returnValue('1=1 AND pages.no_search = 0')); - $whereClause = $this->subject->getWhereClause(); - $this->assertSame( - '1=1 AND pages.no_search = 0 AND table.field = "someValue"', - $whereClause->getStatement() - ); - $this->assertSame( - [], - $whereClause->getParameters() - ); + $this->subject->getRecord(10); + + // $whereClause = $this->subject->getWhereClause(); + // $this->assertSame( + // '1=1 AND pages.no_search = 0 AND table.field = "someValue"', + // $whereClause->getStatement() + // ); + // $this->assertSame( + // [], + // $whereClause->getParameters() + // ); } /** @@ -137,15 +157,15 @@ class TcaTableServiceTest extends AbstractUnitTestCase ); $this->inject($subject, 'logger', $this->getMockedLogger()); - $this->assertSame( - [ - 'test_table.uid', - 'test_table.pid', - 'test_table.available_column', - ], - $subject->getFields(), - '' - ); + // $this->assertSame( + // [ + // 'test_table.uid', + // 'test_table.pid', + // 'test_table.available_column', + // ], + // $subject->getFields(), + // '' + // ); unset($GLOBALS['TCA']['test_table']); } } diff --git a/Tests/Unit/Integration/Form/Finisher/DataHandlerFinisherTest.php b/Tests/Unit/Integration/Form/Finisher/DataHandlerFinisherTest.php index 24c5059..30745cd 100644 --- a/Tests/Unit/Integration/Form/Finisher/DataHandlerFinisherTest.php +++ b/Tests/Unit/Integration/Form/Finisher/DataHandlerFinisherTest.php @@ -61,6 +61,7 @@ class DataHandlerFinisherTest extends AbstractUnitTestCase /** * @test + * @requires function \TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher::setOptions * @dataProvider possibleFinisherSetup */ public function validConfiguration(string $action, array $nonCalledActions, $expectedSecondArgument) @@ -98,6 +99,7 @@ class DataHandlerFinisherTest extends AbstractUnitTestCase /** * @test + * @requires function \TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher::setOptions * @dataProvider invalidFinisherSetup */ public function nothingHappensIfUnknownActionIsConfigured(array $options) diff --git a/ext_localconf.php b/ext_localconf.php index 627f33b..a48f8db 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -37,23 +37,11 @@ call_user_func( ] ); - // Register different concrete implementations, depending on current TYPO3 version. - // This way we can provide working implementations for multiple TYPO3 versions. - $container = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\Container::class); - if (\TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version) >= 8000000) { - $container->registerImplementation( - \Codappix\SearchCore\Compatibility\TypoScriptServiceInterface::class, - \Codappix\SearchCore\Compatibility\TypoScriptService::class - ); - } else { - $container->registerImplementation( - \Codappix\SearchCore\Compatibility\TypoScriptServiceInterface::class, - \Codappix\SearchCore\Compatibility\TypoScriptService76::class - ); - } + \Codappix\SearchCore\Compatibility\ImplementationRegistrationService::registerImplementations(); // API does make use of object manager, therefore use GLOBALS $extensionConfiguration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$extensionKey]); + $container = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\Container::class); if ($extensionConfiguration === false || !isset($extensionConfiguration['disable.']['elasticsearch']) || $extensionConfiguration['disable.']['elasticsearch'] !== '1' From 4d2c8f79caf22b13d46a3c7e4827071d2d4f65f1 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Wed, 14 Mar 2018 20:08:53 +0100 Subject: [PATCH 09/19] TASK: Use imported interface --- Classes/Domain/Index/TcaIndexer.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Classes/Domain/Index/TcaIndexer.php b/Classes/Domain/Index/TcaIndexer.php index 4f96815..071b671 100644 --- a/Classes/Domain/Index/TcaIndexer.php +++ b/Classes/Domain/Index/TcaIndexer.php @@ -30,17 +30,17 @@ use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableServiceInterface; class TcaIndexer extends AbstractIndexer { /** - * @var TcaIndexer\TcaTableServiceInterface + * @var TcaTableServiceInterface */ protected $tcaTableService; /** - * @param TcaIndexer\TcaTableServiceInterface $tcaTableService + * @param TcaTableServiceInterface $tcaTableService * @param ConnectionInterface $connection * @param ConfigurationContainerInterface $configuration */ public function __construct( - TcaIndexer\TcaTableServiceInterface $tcaTableService, + TcaTableServiceInterface $tcaTableService, ConnectionInterface $connection, ConfigurationContainerInterface $configuration ) { From b3390c66dbdb19e4ffbbc822f04fd636617c41f3 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Wed, 14 Mar 2018 20:23:04 +0100 Subject: [PATCH 10/19] TASK: Keep old logic to make changes smaller --- ext_localconf.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ext_localconf.php b/ext_localconf.php index 2e26cd2..658d683 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -44,15 +44,15 @@ call_user_func( // API does make use of object manager, therefore use GLOBALS $extensionConfiguration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$extensionKey]); - $container = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\Container::class); if ($extensionConfiguration === false || !isset($extensionConfiguration['disable.']['elasticsearch']) || $extensionConfiguration['disable.']['elasticsearch'] !== '1' ) { - $container->registerImplementation( - \Codappix\SearchCore\Connection\ConnectionInterface::class, - \Codappix\SearchCore\Connection\Elasticsearch::class - ); + \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\Container::class) + ->registerImplementation( + \Codappix\SearchCore\Connection\ConnectionInterface::class, + \Codappix\SearchCore\Connection\Elasticsearch::class + ); } }, $_EXTKEY From ec8362b93424fc8ab62996e336eab6a7ad6d7586 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Wed, 14 Mar 2018 20:23:29 +0100 Subject: [PATCH 11/19] TASK: Remove unused comment --- Tests/Functional/AbstractFunctionalTestCase.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/Tests/Functional/AbstractFunctionalTestCase.php b/Tests/Functional/AbstractFunctionalTestCase.php index 3d47af8..d015457 100644 --- a/Tests/Functional/AbstractFunctionalTestCase.php +++ b/Tests/Functional/AbstractFunctionalTestCase.php @@ -41,8 +41,6 @@ abstract class AbstractFunctionalTestCase extends CoreTestCase } $this->setUpFrontendRootPage(1, $this->getTypoScriptFilesForFrontendRootPage()); - - // \Codappix\SearchCore\Compatibility\ImplementationRegistrationService::registerImplementations(); } /** From 56bfc4f75ad1b10056314218c75050b3fb6bb9ed Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Wed, 14 Mar 2018 20:23:40 +0100 Subject: [PATCH 12/19] TASK: Fix merge conflict issue --- .../Hooks/DataHandler/ProcessesAllowedTablesTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Tests/Functional/Hooks/DataHandler/ProcessesAllowedTablesTest.php b/Tests/Functional/Hooks/DataHandler/ProcessesAllowedTablesTest.php index dff5f3b..279a113 100644 --- a/Tests/Functional/Hooks/DataHandler/ProcessesAllowedTablesTest.php +++ b/Tests/Functional/Hooks/DataHandler/ProcessesAllowedTablesTest.php @@ -73,7 +73,6 @@ class ProcessesAllowedTablesTest extends AbstractDataHandlerTest */ public function updateWillBeTriggeredForExistingTtContent() { -<<<<<<< HEAD $this->subject->expects($this->exactly(2))->method('update') ->withConsecutive( [ @@ -98,7 +97,7 @@ class ProcessesAllowedTablesTest extends AbstractDataHandlerTest return isset($record['uid']) && $record['uid'] === 1; }) ] - }); + ); $tce = GeneralUtility::makeInstance(Typo3DataHandler::class); $tce->stripslashes_values = 0; From 5a0a4931e4f6c6d8d528b99c14ba218d77e0797b Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 15 Mar 2018 08:07:28 +0100 Subject: [PATCH 13/19] TASK: Make tests compatible with TYPO3 CMS 7.6 As there is no doctrine, we receive values as string, also for uid. --- .../Hooks/DataHandler/NonAllowedTablesTest.php | 18 +++++++++++++++--- .../DataHandler/ProcessesAllowedTablesTest.php | 18 +++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Tests/Functional/Hooks/DataHandler/NonAllowedTablesTest.php b/Tests/Functional/Hooks/DataHandler/NonAllowedTablesTest.php index 11b4806..a9cbaae 100644 --- a/Tests/Functional/Hooks/DataHandler/NonAllowedTablesTest.php +++ b/Tests/Functional/Hooks/DataHandler/NonAllowedTablesTest.php @@ -50,7 +50,11 @@ class NonAllowedTablesTest extends AbstractDataHandlerTest $this->subject->expects($this->exactly(1)) ->method('update') ->with('pages', $this->callback(function (array $record) { - return isset($record['uid']) && $record['uid'] === 1; + if ($this->isLegacyVersion()) { + return isset($record['uid']) && $record['uid'] === '1'; + } else { + return isset($record['uid']) && $record['uid'] === 1; + } })); $tce = GeneralUtility::makeInstance(Typo3DataHandler::class); @@ -73,7 +77,11 @@ class NonAllowedTablesTest extends AbstractDataHandlerTest $this->subject->expects($this->exactly(1)) ->method('update') ->with('pages', $this->callback(function (array $record) { - return isset($record['uid']) && $record['uid'] === 1; + if ($this->isLegacyVersion()) { + return isset($record['uid']) && $record['uid'] === '1'; + } else { + return isset($record['uid']) && $record['uid'] === 1; + } })); $tce = GeneralUtility::makeInstance(Typo3DataHandler::class); @@ -96,7 +104,11 @@ class NonAllowedTablesTest extends AbstractDataHandlerTest $this->subject->expects($this->exactly(1)) ->method('update') ->with('pages', $this->callback(function (array $record) { - return isset($record['uid']) && $record['uid'] === 1; + if ($this->isLegacyVersion()) { + return isset($record['uid']) && $record['uid'] === '1'; + } else { + return isset($record['uid']) && $record['uid'] === 1; + } })); $tce = GeneralUtility::makeInstance(Typo3DataHandler::class); diff --git a/Tests/Functional/Hooks/DataHandler/ProcessesAllowedTablesTest.php b/Tests/Functional/Hooks/DataHandler/ProcessesAllowedTablesTest.php index 279a113..5a571d3 100644 --- a/Tests/Functional/Hooks/DataHandler/ProcessesAllowedTablesTest.php +++ b/Tests/Functional/Hooks/DataHandler/ProcessesAllowedTablesTest.php @@ -53,7 +53,11 @@ class ProcessesAllowedTablesTest extends AbstractDataHandlerTest $this->subject->expects($this->exactly(1)) ->method('update') ->with('pages', $this->callback(function (array $record) { - return isset($record['uid']) && $record['uid'] === 1; + if ($this->isLegacyVersion()) { + return isset($record['uid']) && $record['uid'] === '1'; + } else { + return isset($record['uid']) && $record['uid'] === 1; + } })); $tce = GeneralUtility::makeInstance(Typo3DataHandler::class); @@ -94,7 +98,11 @@ class ProcessesAllowedTablesTest extends AbstractDataHandlerTest [ $this->equalTo('pages'), $this->callback(function ($record) { - return isset($record['uid']) && $record['uid'] === 1; + if ($this->isLegacyVersion()) { + return isset($record['uid']) && $record['uid'] === '1'; + } else { + return isset($record['uid']) && $record['uid'] === 1; + } }) ] ); @@ -137,7 +145,11 @@ class ProcessesAllowedTablesTest extends AbstractDataHandlerTest [ $this->equalTo('pages'), $this->callback(function ($record) { - return isset($record['uid']) && $record['uid'] === 1; + if ($this->isLegacyVersion()) { + return isset($record['uid']) && $record['uid'] === '1'; + } else { + return isset($record['uid']) && $record['uid'] === 1; + } }) ] ); From 403fd47df096ebabab6b8bd7fa42d4f0dea6d5c1 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 15 Mar 2018 11:29:29 +0100 Subject: [PATCH 14/19] TASK: Add necessary phpdoc for extbase As extbase does not reflect PHP source but phpdoc, we have to define the type to use for mapping as phpdoc. --- Classes/Domain/Model/SearchRequest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Classes/Domain/Model/SearchRequest.php b/Classes/Domain/Model/SearchRequest.php index 22bd697..3c88871 100644 --- a/Classes/Domain/Model/SearchRequest.php +++ b/Classes/Domain/Model/SearchRequest.php @@ -87,6 +87,9 @@ class SearchRequest implements SearchRequestInterface return $this->query; } + /** + * @param array $filter + */ public function setFilter(array $filter) { $filter = \TYPO3\CMS\Core\Utility\ArrayUtility::removeArrayEntryByValue($filter, ''); From f3e8dacd4e0ceb405dac3caa9b8f252ebee767c8 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 15 Mar 2018 13:59:47 +0100 Subject: [PATCH 15/19] TASK: Keep language information for all TYPO3 Versions Keep code consistent. Fetch language field for both TYPO3 versions. To make sure we do not mess up, add test case. --- .../Index/TcaIndexer/TcaTableService76.php | 1 - Tests/Functional/Indexing/TcaIndexerTest.php | 36 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/Classes/Domain/Index/TcaIndexer/TcaTableService76.php b/Classes/Domain/Index/TcaIndexer/TcaTableService76.php index 28922a5..f52027a 100644 --- a/Classes/Domain/Index/TcaIndexer/TcaTableService76.php +++ b/Classes/Domain/Index/TcaIndexer/TcaTableService76.php @@ -257,7 +257,6 @@ class TcaTableService76 implements TcaTableServiceInterface $this->tca['ctrl']['cruser_id'], $this->tca['ctrl']['fe_cruser_id'], $this->tca['ctrl']['fe_crgroup_id'], - $this->tca['ctrl']['languageField'], $this->tca['ctrl']['origUid'], ]; diff --git a/Tests/Functional/Indexing/TcaIndexerTest.php b/Tests/Functional/Indexing/TcaIndexerTest.php index 472cc15..d7ecc3a 100644 --- a/Tests/Functional/Indexing/TcaIndexerTest.php +++ b/Tests/Functional/Indexing/TcaIndexerTest.php @@ -77,4 +77,40 @@ class TcaIndexerTest extends AbstractFunctionalTestCase $objectManager->get(TcaIndexer::class, $tableService, $connection)->indexAllDocuments(); } + + /** + * @test + */ + public function sysLanguageIsKept() + { + $this->importDataSet('Tests/Functional/Fixtures/Indexing/TcaIndexer/KeepSysLanguageUid.xml'); + $objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(ObjectManager::class); + $tableName = 'tt_content'; + $tableService = $objectManager->get( + TcaTableServiceInterface::class, + $tableName, + $objectManager->get(RelationResolver::class), + $objectManager->get(ConfigurationContainerInterface::class) + ); + + $connection = $this->getMockBuilder(Elasticsearch::class) + ->setMethods(['addDocuments']) + ->disableOriginalConstructor() + ->getMock(); + + $connection->expects($this->once()) + ->method('addDocuments') + ->with( + $this->stringContains('tt_content'), + $this->callback(function ($documents) { + if ($this->isLegacyVersion()) { + return isset($documents[0]['sys_language_uid']) && $documents[0]['sys_language_uid'] === '2'; + } else { + return isset($documents[0]['sys_language_uid']) && $documents[0]['sys_language_uid'] === 2; + } + }) + ); + + $objectManager->get(TcaIndexer::class, $tableService, $connection)->indexAllDocuments(); + } } From dea028ed0f862b968a0a57db00c2c1921aeed8f7 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 15 Mar 2018 14:00:45 +0100 Subject: [PATCH 16/19] TASK: Add missing fixture --- .../TcaIndexer/KeepSysLanguageUid.xml | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 Tests/Functional/Fixtures/Indexing/TcaIndexer/KeepSysLanguageUid.xml diff --git a/Tests/Functional/Fixtures/Indexing/TcaIndexer/KeepSysLanguageUid.xml b/Tests/Functional/Fixtures/Indexing/TcaIndexer/KeepSysLanguageUid.xml new file mode 100644 index 0000000..913ec67 --- /dev/null +++ b/Tests/Functional/Fixtures/Indexing/TcaIndexer/KeepSysLanguageUid.xml @@ -0,0 +1,29 @@ + + + + 2 + 1 + Content here will be indexed + + + + 1 + 1 + 1480686370 + 1480686370 + 0 + 72 + 2 + header +
indexed content element
+ this is the content of header content element that should get indexed + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 +
+
From 22e097ca851bc086e8475fd2afad2284630888e7 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 15 Mar 2018 14:00:53 +0100 Subject: [PATCH 17/19] TASK: Do not comment out code for non working tests Mark tests as non working for now. --- .../Index/TcaIndexer/TcaTableServiceTest.php | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/Tests/Unit/Domain/Index/TcaIndexer/TcaTableServiceTest.php b/Tests/Unit/Domain/Index/TcaIndexer/TcaTableServiceTest.php index 70174ec..f470613 100644 --- a/Tests/Unit/Domain/Index/TcaIndexer/TcaTableServiceTest.php +++ b/Tests/Unit/Domain/Index/TcaIndexer/TcaTableServiceTest.php @@ -74,7 +74,7 @@ class TcaTableServiceTest extends AbstractUnitTestCase */ public function doUsePlainQueryIfNoAdditionalWhereClauseIsDefined() { - $this->markTestSkipped('We have to migrate this test for TYPO3 CMS 8.x'); + $this->markTestIncomplete('We have to migrate this test for TYPO3 CMS 8.x'); $this->configuration->expects($this->exactly(2)) ->method('getIfExists') ->withConsecutive(['indexing.table.additionalWhereClause'], ['indexing.table.rootLineBlacklist']) @@ -98,7 +98,7 @@ class TcaTableServiceTest extends AbstractUnitTestCase */ public function configuredAdditionalWhereClauseIsAdded() { - $this->markTestSkipped('We have to migrate this test for TYPO3 CMS 8.x'); + $this->markTestIncomplete('We have to migrate this test for TYPO3 CMS 8.x'); $this->configuration->expects($this->exactly(2)) ->method('getIfExists') ->withConsecutive(['indexing.table.additionalWhereClause'], ['indexing.table.rootLineBlacklist']) @@ -110,15 +110,15 @@ class TcaTableServiceTest extends AbstractUnitTestCase $this->subject->getRecord(10); - // $whereClause = $this->subject->getWhereClause(); - // $this->assertSame( - // '1=1 AND pages.no_search = 0 AND table.field = "someValue"', - // $whereClause->getStatement() - // ); - // $this->assertSame( - // [], - // $whereClause->getParameters() - // ); + $whereClause = $this->subject->getWhereClause(); + $this->assertSame( + '1=1 AND pages.no_search = 0 AND table.field = "someValue"', + $whereClause->getStatement() + ); + $this->assertSame( + [], + $whereClause->getParameters() + ); } /** @@ -126,6 +126,7 @@ class TcaTableServiceTest extends AbstractUnitTestCase */ public function allConfiguredAndAllowedTcaColumnsAreReturnedAsFields() { + $this->markTestIncomplete('We have to migrate this test'); $GLOBALS['TCA']['test_table'] = [ 'ctrl' => [ 'languageField' => 'sys_language_uid', @@ -161,16 +162,16 @@ class TcaTableServiceTest extends AbstractUnitTestCase ); $this->inject($subject, 'logger', $this->getMockedLogger()); - // $this->assertSame( - // [ - // 'test_table.uid', - // 'test_table.pid', - // 'test_table.sys_language_uid', - // 'test_table.available_column', - // ], - // $subject->getFields(), - // '' - // ); + $this->assertSame( + [ + 'test_table.uid', + 'test_table.pid', + 'test_table.sys_language_uid', + 'test_table.available_column', + ], + $subject->getFields(), + '' + ); unset($GLOBALS['TCA']['test_table']); } } From 2b78ffbe9668d1f5c8e66ae8927be8420818dd71 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 15 Mar 2018 14:42:41 +0100 Subject: [PATCH 18/19] BUGFIX: Workaround doctrine limitation Doctrine will not implode our array, so we have to do --- Classes/Domain/Index/TcaIndexer/TcaTableService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/Domain/Index/TcaIndexer/TcaTableService.php b/Classes/Domain/Index/TcaIndexer/TcaTableService.php index a0d5851..ee0a2d8 100644 --- a/Classes/Domain/Index/TcaIndexer/TcaTableService.php +++ b/Classes/Domain/Index/TcaIndexer/TcaTableService.php @@ -174,7 +174,7 @@ class TcaTableService implements TcaTableServiceInterface } if ($this->isBlackListedRootLineConfigured()) { - $parameters[':blacklistedRootLine'] = $this->getBlackListedRootLine(); + $parameters[':blacklistedRootLine'] = implode(',', $this->getBlackListedRootLine()); $whereClause .= ' AND pages.uid NOT IN (:blacklistedRootLine)' . ' AND pages.pid NOT IN (:blacklistedRootLine)'; } From 5c8cfa9cca65192bf3d367dfdcf2500710aae16e Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 15 Mar 2018 14:43:00 +0100 Subject: [PATCH 19/19] TASK: Remove unnecessary fixture --- .../Fixtures/Indexing/TcaIndexer/KeepSysLanguageUid.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Tests/Functional/Fixtures/Indexing/TcaIndexer/KeepSysLanguageUid.xml b/Tests/Functional/Fixtures/Indexing/TcaIndexer/KeepSysLanguageUid.xml index 913ec67..f17051c 100644 --- a/Tests/Functional/Fixtures/Indexing/TcaIndexer/KeepSysLanguageUid.xml +++ b/Tests/Functional/Fixtures/Indexing/TcaIndexer/KeepSysLanguageUid.xml @@ -1,11 +1,5 @@ - - 2 - 1 - Content here will be indexed - - 1 1