diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d88c47a88..147ca12f5f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -162,33 +162,8 @@ jobs: php-version: ${{ matrix.PHP }} coverage: pcov tools: composer:2.5.5 - - - name: Resolve CI build cache key - # CI_CACHE_VERSION is used and can be increased to be able to invalidate caches. - # For example if some composer dependencies added or removed in composer.json or - # in Build/Test/bootstrap.sh - run: | - export CURRENT_TYPO3_VERSION_REFERNCE=$(./Build/Helpers/TYPO3_SOURCE_REFERENCE.sh "$TYPO3_VERSION" --short) - export CURRENT_SOLARIUM_VERSION=$(cat composer.json | jq --raw-output '.require."solarium/solarium"') - export CI_CACHE_VERSION="2022.12.22@20:00" - export CI_BUILD_CACHE_KEY=${{ runner.os }}-PHP:${{ matrix.PHP }}-TYPO3:$TYPO3_VERSION@$CURRENT_TYPO3_VERSION_REFERNCE-SOLARIUM:$CURRENT_SOLARIUM_VERSION-"CI_CACHE_VERSION:"$CI_CACHE_VERSION - echo "COMPOSER_GLOBAL_REQUEREMENTS=$(composer config home)" >> $GITHUB_ENV - echo "CI_BUILD_CACHE_KEY=$CI_BUILD_CACHE_KEY" >> $GITHUB_ENV - echo "The key for actions/cache@v2 is \"$CI_BUILD_CACHE_KEY\"" - - - name: Restore ci build caches - id: restore_ci_build_caches - uses: actions/cache@v2 - with: - path: | - ${{ env.CI_BUILD_DIRECTORY }}/Web - ${{ env.CI_BUILD_DIRECTORY }}/bin - ${{ env.CI_BUILD_DIRECTORY }}/vendor - ${{ env.COMPOSER_GLOBAL_REQUEREMENTS }} - key: ${{ env.CI_BUILD_CACHE_KEY }} - name: CI-Bootstrap - if: steps.restore_ci_build_caches.outputs.cache-hit != 'true' run: | ./Build/Test/bootstrap.sh --skip-solr-install echo "Current Size of EXT:Solr build Artefacts before run: " \ diff --git a/Classes/ConnectionManager.php b/Classes/ConnectionManager.php index ad0863e89e..c20b5859c2 100644 --- a/Classes/ConnectionManager.php +++ b/Classes/ConnectionManager.php @@ -21,11 +21,11 @@ use ApacheSolrForTypo3\Solr\Domain\Site\Site; use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository; use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository as PagesRepositoryAtExtSolr; -use ApacheSolrForTypo3\Solr\System\Solr\Node; use ApacheSolrForTypo3\Solr\System\Solr\SolrConnection; use ApacheSolrForTypo3\Solr\System\Util\SiteUtility; use Doctrine\DBAL\Exception as DBALException; use InvalidArgumentException; +use Solarium\Core\Client\Endpoint; use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\Site\Entity\Site as Typo3Site; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -63,8 +63,8 @@ public function getSolrConnectionForNodes(array $readNodeConfiguration, array $w { $connectionHash = md5(json_encode($readNodeConfiguration) . json_encode($writeNodeConfiguration)); if (!isset(self::$connections[$connectionHash])) { - $readNode = Node::fromArray($readNodeConfiguration); - $writeNode = Node::fromArray($writeNodeConfiguration); + $readNode = new Endpoint($readNodeConfiguration); + $writeNode = new Endpoint($writeNodeConfiguration); self::$connections[$connectionHash] = GeneralUtility::makeInstance(SolrConnection::class, $readNode, $writeNode); } return self::$connections[$connectionHash]; diff --git a/Classes/Domain/Index/PageIndexer/Helper/UriBuilder/AbstractUriStrategy.php b/Classes/Domain/Index/PageIndexer/Helper/UriBuilder/AbstractUriStrategy.php deleted file mode 100644 index a39e8d3d17..0000000000 --- a/Classes/Domain/Index/PageIndexer/Helper/UriBuilder/AbstractUriStrategy.php +++ /dev/null @@ -1,133 +0,0 @@ -logger = $logger ?? GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__); - } - - protected function applyTypoScriptOverridesOnIndexingUrl(UrlHelper $urlHelper, array $overrideConfiguration = []): UrlHelper - { - // check whether we should use ssl / https - if (!empty($overrideConfiguration['scheme'])) { - $urlHelper = $urlHelper->withScheme($overrideConfiguration['scheme']); - } - - // overwriting the host - if (!empty($overrideConfiguration['host'])) { - $urlHelper = $urlHelper->withHost($overrideConfiguration['host']); - } - - // overwriting the port - if (!empty($overrideConfiguration['port'])) { - $urlHelper = $urlHelper->withPort((int)$overrideConfiguration['port']); - } - - // setting a path if TYPO3 is installed in a subdirectory - if (!empty($overrideConfiguration['path'])) { - $urlHelper = $urlHelper->withPath($overrideConfiguration['path']); - } - - return $urlHelper; - } - - public function getPageIndexingUriFromPageItemAndLanguageId( - Item $item, - int $language = 0, - string $mountPointParameter = '', - array $options = [] - ): string { - $pageIndexUri = $this->buildPageIndexingUriFromPageItemAndLanguageId($item, $language, $mountPointParameter); - $urlHelper = GeneralUtility::makeInstance(UrlHelper::class, $pageIndexUri); - $overrideConfiguration = $options['frontendDataHelper.'] ?? []; - $urlHelper = $this->applyTypoScriptOverridesOnIndexingUrl($urlHelper, $overrideConfiguration); - $dataUrl = (string)$urlHelper; - - if (!GeneralUtility::isValidUrl($dataUrl)) { - $this->logger->log( - SolrLogManager::ERROR, - 'Could not create a valid URL to get frontend data while trying to index a page.', - [ - 'item' => (array)$item, - 'constructed URL' => $dataUrl, - 'scheme' => $urlHelper->getScheme(), - 'host' => $urlHelper->getHost(), - 'path' => $urlHelper->getPath(), - 'page ID' => $item->getRecordUid(), - 'indexer options' => $options, - ] - ); - - throw new RuntimeException( - 'Could not create a valid URL to get frontend data while trying to index a page. Created URL: ' . $dataUrl, - 1311080805 - ); - } - - return $this->applyDataUrlModifier($item, $language, $dataUrl, $urlHelper); - } - - abstract protected function buildPageIndexingUriFromPageItemAndLanguageId( - Item $item, - int $language = 0, - string $mountPointParameter = '' - ); - - protected function applyDataUrlModifier( - Item $item, - int $language, - string $dataUrl, - UrlHelper $urlHelper - ): string { - if (empty($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueuePageIndexer']['dataUrlModifier'])) { - return $dataUrl; - } - - $dataUrlModifier = GeneralUtility::makeInstance($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueuePageIndexer']['dataUrlModifier']); - if (!$dataUrlModifier instanceof PageIndexerDataUrlModifier) { - throw new RuntimeException($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueuePageIndexer']['dataUrlModifier'] . ' is not an implementation of ApacheSolrForTypo3\Solr\IndexQueue\PageIndexerDataUrlModifier', 1290523345); - } - - return $dataUrlModifier->modifyDataUrl( - $dataUrl, - [ - 'item' => $item, 'scheme' => $urlHelper->getScheme(), 'host' => $urlHelper->getHost(), - 'path' => $urlHelper->getPath(), 'pageId' => $item->getRecordUid(), 'language' => $language, - ] - ); - } -} diff --git a/Classes/Domain/Index/PageIndexer/Helper/UriBuilder/TYPO3SiteStrategy.php b/Classes/Domain/Index/PageIndexer/Helper/UriBuilder/TYPO3SiteStrategy.php deleted file mode 100644 index 2dea26c3d8..0000000000 --- a/Classes/Domain/Index/PageIndexer/Helper/UriBuilder/TYPO3SiteStrategy.php +++ /dev/null @@ -1,67 +0,0 @@ -siteFinder = $siteFinder ?? GeneralUtility::makeInstance(SiteFinder::class); - } - - /** - * Builds and returns URI for page indexing from index queue item - * Handles "pages" type only. - * - * @throws SiteNotFoundException - */ - protected function buildPageIndexingUriFromPageItemAndLanguageId( - Item $item, - int $language = 0, - string $mountPointParameter = '', - ): string { - $site = $this->siteFinder->getSiteByPageId($item->getRecordUid()); - $parameters = []; - - if ($language > 0) { - $parameters['_language'] = $language; - } - - if ($mountPointParameter !== '') { - $parameters['MP'] = $mountPointParameter; - } - - return (string)$site->getRouter()->generateUri($item->getRecord(), $parameters); - } -} diff --git a/Classes/Domain/Index/PageIndexer/Helper/UriStrategyFactory.php b/Classes/Domain/Index/PageIndexer/Helper/UriStrategyFactory.php deleted file mode 100644 index d82803d896..0000000000 --- a/Classes/Domain/Index/PageIndexer/Helper/UriStrategyFactory.php +++ /dev/null @@ -1,50 +0,0 @@ -logger = $logger ?? GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__); + $this->siteFinder = $siteFinder ?? GeneralUtility::makeInstance(SiteFinder::class); + $this->eventDispatcher = $eventDispatcher ?? GeneralUtility::makeInstance(EventDispatcherInterface::class); + } + + /** + * Builds and returns URI for page indexing from index queue item + * Handles "pages" type only. + * + * @throws SiteNotFoundException + */ + protected function buildPageIndexingUriFromPageItemAndLanguageId( + Item $item, + int $language = 0, + string $mountPointParameter = '', + ): UriInterface { + $site = $this->siteFinder->getSiteByPageId($item->getRecordUid()); + $parameters = []; + + if ($language > 0) { + $parameters['_language'] = $language; + } + + if ($mountPointParameter !== '') { + $parameters['MP'] = $mountPointParameter; + } + + return $site->getRouter()->generateUri($item->getRecord(), $parameters); + } + + protected function applyTypoScriptOverridesOnIndexingUrl(UriInterface $urlHelper, array $overrideConfiguration): UriInterface + { + // check whether we should use ssl / https + if (!empty($overrideConfiguration['scheme'])) { + $urlHelper = $urlHelper->withScheme($overrideConfiguration['scheme']); + } + + // overwriting the host + if (!empty($overrideConfiguration['host'])) { + $urlHelper = $urlHelper->withHost($overrideConfiguration['host']); + } + + // overwriting the port + if (!empty($overrideConfiguration['port'])) { + $urlHelper = $urlHelper->withPort((int)$overrideConfiguration['port']); + } + + // setting a path if TYPO3 is installed in a subdirectory + if (!empty($overrideConfiguration['path'])) { + $urlHelper = $urlHelper->withPath($overrideConfiguration['path']); + } + + return $urlHelper; + } + + public function getPageIndexingUriFromPageItemAndLanguageId( + Item $item, + int $language = 0, + string $mountPointParameter = '', + array $options = [] + ): string { + $pageIndexUri = $this->buildPageIndexingUriFromPageItemAndLanguageId($item, $language, $mountPointParameter); + $overrideConfiguration = $options['frontendDataHelper.'] ?? []; + $pageIndexUri = $this->applyTypoScriptOverridesOnIndexingUrl($pageIndexUri, $overrideConfiguration); + + if (!GeneralUtility::isValidUrl((string)$pageIndexUri)) { + $this->logger->log( + SolrLogManager::ERROR, + 'Could not create a valid URL to get frontend data while trying to index a page.', + [ + 'item' => (array)$item, + 'constructed URL' => (string)$pageIndexUri, + 'scheme' => $pageIndexUri->getScheme(), + 'host' => $pageIndexUri->getHost(), + 'path' => $pageIndexUri->getPath(), + 'page ID' => $item->getRecordUid(), + 'indexer options' => $options, + ] + ); + + throw new \RuntimeException( + 'Could not create a valid URL to get frontend data while trying to index a page. Created URL: ' . (string)$pageIndexUri, + 1311080805 + ); + } + + $event = new AfterFrontendPageUriForIndexingHasBeenGeneratedEvent($item, $pageIndexUri, $language, $mountPointParameter, $options); + $event = $this->eventDispatcher->dispatch($event); + return (string)$event->getPageIndexUri(); + } +} diff --git a/Classes/Domain/Index/Queue/QueueInitializationService.php b/Classes/Domain/Index/Queue/QueueInitializationService.php index 5a0ab675bd..33b1524713 100644 --- a/Classes/Domain/Index/Queue/QueueInitializationService.php +++ b/Classes/Domain/Index/Queue/QueueInitializationService.php @@ -18,13 +18,13 @@ namespace ApacheSolrForTypo3\Solr\Domain\Index\Queue; use ApacheSolrForTypo3\Solr\Domain\Site\Site; -use ApacheSolrForTypo3\Solr\IndexQueue\InitializationPostProcessor; +use ApacheSolrForTypo3\Solr\Event\IndexQueue\AfterIndexQueueHasBeenInitializedEvent; use ApacheSolrForTypo3\Solr\IndexQueue\Initializer\AbstractInitializer; use ApacheSolrForTypo3\Solr\IndexQueue\Queue; use Doctrine\DBAL\ConnectionException; use Doctrine\DBAL\Exception as DBALException; +use Psr\EventDispatcher\EventDispatcherInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; -use UnexpectedValueException; /** * The queue initialization service is responsible to run the initialization of the index queue for a combination of sites @@ -36,10 +36,12 @@ class QueueInitializationService { protected Queue $queue; + protected EventDispatcherInterface $eventDispatcher; - public function __construct(Queue $queue) + public function __construct(Queue $queue, EventDispatcherInterface $eventDispatcher = null) { $this->queue = $queue; + $this->eventDispatcher = $eventDispatcher ?? GeneralUtility::makeInstance(EventDispatcherInterface::class); } /** @@ -85,6 +87,8 @@ public function initializeBySitesAndConfigurations(array $sites, array $indexing * Initializes a set of index configurations for a given site. * If one of the indexing configuration names is a * (wildcard) all configurations are used, * + * @param array $indexingConfigurationNames + * @return array * @throws ConnectionException * @throws DBALException */ @@ -97,20 +101,6 @@ public function initializeBySiteAndIndexConfigurations(Site $site, array $indexi foreach ($indexingConfigurationNames as $indexingConfigurationName) { $initializationStatus[$indexingConfigurationName] = $this->applyInitialization($site, (string)$indexingConfigurationName); } - - if (!isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessIndexQueueInitialization'])) { - return $initializationStatus; - } - - foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessIndexQueueInitialization'] as $classReference) { - $indexQueueInitializationPostProcessor = GeneralUtility::makeInstance($classReference); - if ($indexQueueInitializationPostProcessor instanceof InitializationPostProcessor) { - $indexQueueInitializationPostProcessor->postProcessIndexQueueInitialization($site, $indexingConfigurationNames, $initializationStatus); - } else { - throw new UnexpectedValueException(get_class($indexQueueInitializationPostProcessor) . ' must implement interface ' . InitializationPostProcessor::class, 1345815561); - } - } - return $initializationStatus; } @@ -136,8 +126,6 @@ protected function applyInitialization(Site $site, string $indexingConfiguration /** * Executes desired initializer - * - * @throws DBALException */ protected function executeInitializer( Site $site, @@ -153,6 +141,9 @@ protected function executeInitializer( $initializer->setIndexingConfigurationName($indexingConfigurationName); $initializer->setIndexingConfiguration($indexConfiguration); - return $initializer->initialize(); + $isInitialized = $initializer->initialize(); + $event = new AfterIndexQueueHasBeenInitializedEvent($initializer, $site, $indexingConfigurationName, $type, $indexConfiguration, $isInitialized); + $event = $this->eventDispatcher->dispatch($event); + return $event->isInitialized(); } } diff --git a/Classes/Domain/Index/Queue/QueueItemRepository.php b/Classes/Domain/Index/Queue/QueueItemRepository.php index a9bad5f8a4..652055825b 100644 --- a/Classes/Domain/Index/Queue/QueueItemRepository.php +++ b/Classes/Domain/Index/Queue/QueueItemRepository.php @@ -18,12 +18,14 @@ namespace ApacheSolrForTypo3\Solr\Domain\Index\Queue; use ApacheSolrForTypo3\Solr\Domain\Site\Site; +use ApacheSolrForTypo3\Solr\Event\IndexQueue\AfterRecordsForIndexQueueItemsHaveBeenRetrievedEvent; use ApacheSolrForTypo3\Solr\IndexQueue\Item; use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager; use ApacheSolrForTypo3\Solr\System\Records\AbstractRepository; use ApacheSolrForTypo3\Solr\System\Util\SiteUtility; use Doctrine\DBAL\Exception as DBALException; use PDO; +use Psr\EventDispatcher\EventDispatcherInterface; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression; use TYPO3\CMS\Core\Database\Query\QueryBuilder; @@ -38,13 +40,15 @@ class QueueItemRepository extends AbstractRepository protected string $table = 'tx_solr_indexqueue_item'; protected SolrLogManager $logger; + protected EventDispatcherInterface $eventDispatcher; - public function __construct(SolrLogManager $logManager = null) + public function __construct(SolrLogManager $logManager = null, EventDispatcherInterface $eventDispatcher = null) { $this->logger = $logManager ?? GeneralUtility::makeInstance( SolrLogManager::class, __CLASS__ ); + $this->eventDispatcher = $eventDispatcher ?? GeneralUtility::makeInstance(EventDispatcherInterface::class); } /** @@ -699,26 +703,13 @@ protected function getAllQueueItemRecordsByUidsGroupedByTable(array $indexQueueI } $tableRecords[$table] = $records; - $this->hookPostProcessFetchRecordsForIndexQueueItem($table, $uids, $tableRecords); + $event = $this->eventDispatcher->dispatch(new AfterRecordsForIndexQueueItemsHaveBeenRetrievedEvent($table, $uids, $records)); + $tableRecords[$table] = $event->getRecords(); } return $tableRecords; } - /** - * Calls defined in postProcessFetchRecordsForIndexQueueItem hook method. - */ - protected function hookPostProcessFetchRecordsForIndexQueueItem(string $table, array $uids, array &$tableRecords): void - { - if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessFetchRecordsForIndexQueueItem'] ?? null)) { - return; - } - $params = ['table' => $table, 'uids' => $uids, 'tableRecords' => &$tableRecords]; - foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessFetchRecordsForIndexQueueItem'] as $reference) { - GeneralUtility::callUserFunction($reference, $params, $this); - } - } - /** * Instantiates a list of Item objects from database records. * diff --git a/Classes/Domain/Search/ApacheSolrDocument/Builder.php b/Classes/Domain/Search/ApacheSolrDocument/Builder.php index 2d73d0bd17..faa08b216a 100644 --- a/Classes/Domain/Search/ApacheSolrDocument/Builder.php +++ b/Classes/Domain/Search/ApacheSolrDocument/Builder.php @@ -25,7 +25,6 @@ use ApacheSolrForTypo3\Solr\Typo3PageContentExtractor; use ApacheSolrForTypo3\Solr\Util; use Doctrine\DBAL\Exception as DBALException; -use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; @@ -45,9 +44,6 @@ public function __construct( /** * This method can be used to build a Document from a TYPO3 page. - * - * @throws AspectNotFoundException - * @throws DBALException */ public function fromPage( TypoScriptFrontendController $page, @@ -55,11 +51,12 @@ public function fromPage( Rootline $pageAccessRootline, string $mountPointParameter = '', ): Document { - /** @var Document $document */ - $document = GeneralUtility::makeInstance(Document::class); - $site = $this->getSiteByPageId($page->id); + $pageId = $page->id; $pageRecord = $page->page; + $document = GeneralUtility::makeInstance(Document::class); + $site = $this->getSiteByPageId($pageId); + $accessGroups = $this->getDocumentIdGroups($pageAccessRootline); $documentId = $this->getPageDocumentId($page, $accessGroups, $mountPointParameter); @@ -70,18 +67,18 @@ public function fromPage( $document->setField('type', 'pages'); // system fields - $document->setField('uid', $page->id); + $document->setField('uid', $pageId); $document->setField('pid', $pageRecord['pid']); // variantId - $variantId = $this->variantIdBuilder->buildFromTypeAndUid('pages', $page->id); + $variantId = $this->variantIdBuilder->buildFromTypeAndUid('pages', $pageId); $document->setField('variantId', $variantId); $document->setField('typeNum', (int)$page->getPageArguments()->getPageType()); $document->setField('created', $pageRecord['crdate']); $document->setField('changed', $pageRecord['SYS_LASTCHANGED']); - $rootline = $this->getRootLineFieldValue($page->id, $mountPointParameter); + $rootline = $this->getRootLineFieldValue($pageId, $mountPointParameter); $document->setField('rootline', $rootline); // access @@ -157,12 +154,11 @@ public function fromRecord(array $itemRecord, string $type, int $rootPageUid, st } /** - * @throws AspectNotFoundException * @throws DBALException */ protected function getPageDocumentId(TypoScriptFrontendController $frontendController, string $accessGroups, string $mountPointParameter): string { - return Util::getPageDocumentId($frontendController->id, (int)$frontendController->getPageArguments()->getPageType(), Util::getLanguageUid(), $accessGroups, $mountPointParameter); + return Util::getPageDocumentId($frontendController->id, (int)$frontendController->getPageArguments()->getPageType(), $frontendController->getLanguage()->getLanguageId(), $accessGroups, $mountPointParameter); } /** diff --git a/Classes/Event/IndexQueue/AfterIndexQueueHasBeenInitializedEvent.php b/Classes/Event/IndexQueue/AfterIndexQueueHasBeenInitializedEvent.php new file mode 100644 index 0000000000..80fbe45648 --- /dev/null +++ b/Classes/Event/IndexQueue/AfterIndexQueueHasBeenInitializedEvent.php @@ -0,0 +1,75 @@ +initializer; + } + + public function getSite(): Site + { + return $this->site; + } + + public function getIndexingConfigurationName(): string + { + return $this->indexingConfigurationName; + } + + public function getType(): string + { + return $this->type; + } + + public function getIndexingConfiguration(): array + { + return $this->indexingConfiguration; + } + + public function isInitialized(): bool + { + return $this->isInitialized; + } + + public function setIsInitialized(bool $isInitialized): void + { + $this->isInitialized = $isInitialized; + } +} diff --git a/Classes/Event/IndexQueue/AfterRecordsForIndexQueueItemsHaveBeenRetrievedEvent.php b/Classes/Event/IndexQueue/AfterRecordsForIndexQueueItemsHaveBeenRetrievedEvent.php new file mode 100644 index 0000000000..71fa47555a --- /dev/null +++ b/Classes/Event/IndexQueue/AfterRecordsForIndexQueueItemsHaveBeenRetrievedEvent.php @@ -0,0 +1,53 @@ +table; + } + + public function getRecordUids(): array + { + return $this->uids; + } + + public function getRecords(): array + { + return $this->records; + } + + public function setRecords(array $records): void + { + $this->records = $records; + } +} diff --git a/Classes/Event/Indexing/AfterFrontendPageUriForIndexingHasBeenGeneratedEvent.php b/Classes/Event/Indexing/AfterFrontendPageUriForIndexingHasBeenGeneratedEvent.php new file mode 100644 index 0000000000..a692ce1b38 --- /dev/null +++ b/Classes/Event/Indexing/AfterFrontendPageUriForIndexingHasBeenGeneratedEvent.php @@ -0,0 +1,69 @@ +item; + } + + public function getPageIndexUri(): UriInterface + { + return $this->pageIndexUri; + } + + public function setPageIndexUri(UriInterface $pageIndexUri): void + { + $this->pageIndexUri = $pageIndexUri; + } + + public function getLanguageId(): int + { + return $this->languageId; + } + + public function getMountPointParameter(): string + { + return $this->mountPointParameter; + } + + public function getOptions(): array + { + return $this->options; + } +} diff --git a/Classes/Event/Indexing/AfterPageDocumentIsCreatedForIndexingEvent.php b/Classes/Event/Indexing/AfterPageDocumentIsCreatedForIndexingEvent.php new file mode 100644 index 0000000000..2a8922ad2a --- /dev/null +++ b/Classes/Event/Indexing/AfterPageDocumentIsCreatedForIndexingEvent.php @@ -0,0 +1,85 @@ +document; + } + + public function overrideDocument(Document $document): void + { + $this->document = $document; + } + + public function getIndexQueueItem(): Item + { + return $this->indexQueueItem; + } + + public function getIndexingConfigurationName(): string + { + return $this->indexQueueItem->getIndexingConfigurationName(); + } + + public function getSite(): Site + { + return $this->site; + } + + public function getSiteLanguage(): SiteLanguage + { + return $this->siteLanguage; + } + + public function getRecord(): array + { + return $this->record; + } + + public function getConfiguration(): TypoScriptConfiguration + { + return $this->configuration; + } +} diff --git a/Classes/AdditionalFieldsIndexer.php b/Classes/EventListener/PageIndexer/AdditionalFieldsForPageIndexing.php similarity index 73% rename from Classes/AdditionalFieldsIndexer.php rename to Classes/EventListener/PageIndexer/AdditionalFieldsForPageIndexing.php index 0e1c9e341d..77221d555a 100644 --- a/Classes/AdditionalFieldsIndexer.php +++ b/Classes/EventListener/PageIndexer/AdditionalFieldsForPageIndexing.php @@ -15,11 +15,10 @@ * The TYPO3 project - inspiring people to share! */ -namespace ApacheSolrForTypo3\Solr; +namespace ApacheSolrForTypo3\Solr\EventListener\PageIndexer; -use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; +use ApacheSolrForTypo3\Solr\Event\Indexing\AfterPageDocumentIsCreatedForIndexingEvent; use ApacheSolrForTypo3\Solr\System\ContentObject\ContentObjectService; -use ApacheSolrForTypo3\Solr\System\Solr\Document\Document; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -32,31 +31,28 @@ * * @author Ingo Renner */ -class AdditionalFieldsIndexer implements SubstitutePageIndexer +class AdditionalFieldsForPageIndexing { - protected TypoScriptConfiguration $configuration; - protected array $additionalIndexingFields = []; protected array $additionalFieldNames = []; protected ContentObjectService $contentObjectService; - public function __construct( - TypoScriptConfiguration $configuration = null, - ContentObjectService $contentObjectService = null - ) { - $this->configuration = $configuration ?? Util::getSolrConfiguration(); - $this->additionalIndexingFields = $this->configuration->getIndexAdditionalFieldsConfiguration(); - $this->additionalFieldNames = $this->configuration->getIndexMappedAdditionalFieldNames(); + public function __construct(ContentObjectService $contentObjectService = null) + { $this->contentObjectService = $contentObjectService ?? GeneralUtility::makeInstance(ContentObjectService::class); } /** * Returns a substituted document for the currently being indexed page. */ - public function getPageDocument(Document $originalPageDocument): Document + public function __invoke(AfterPageDocumentIsCreatedForIndexingEvent $event): void { + $this->additionalIndexingFields = $event->getConfiguration()->getIndexAdditionalFieldsConfiguration(); + $this->additionalFieldNames = $event->getConfiguration()->getIndexMappedAdditionalFieldNames(); + + $originalPageDocument = $event->getDocument(); $substitutePageDocument = clone $originalPageDocument; $additionalFields = $this->getAdditionalFields(); @@ -66,8 +62,7 @@ public function getPageDocument(Document $originalPageDocument): Document $substitutePageDocument->setField($fieldName, $fieldValue); } } - - return $substitutePageDocument; + $event->overrideDocument($substitutePageDocument); } /** diff --git a/Classes/IndexQueue/FrontendHelper/PageFieldMappingIndexer.php b/Classes/IndexQueue/FrontendHelper/PageFieldMappingIndexer.php index 3ccb014369..f34366e70b 100644 --- a/Classes/IndexQueue/FrontendHelper/PageFieldMappingIndexer.php +++ b/Classes/IndexQueue/FrontendHelper/PageFieldMappingIndexer.php @@ -18,12 +18,11 @@ namespace ApacheSolrForTypo3\Solr\IndexQueue\FrontendHelper; // TODO use/extend ApacheSolrForTypo3\Solr\IndexQueue\AbstractIndexer +use ApacheSolrForTypo3\Solr\Event\Indexing\AfterPageDocumentIsCreatedForIndexingEvent; use ApacheSolrForTypo3\Solr\IndexQueue\AbstractIndexer; use ApacheSolrForTypo3\Solr\IndexQueue\InvalidFieldNameException; -use ApacheSolrForTypo3\Solr\SubstitutePageIndexer; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; use ApacheSolrForTypo3\Solr\System\Solr\Document\Document; -use ApacheSolrForTypo3\Solr\Util; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; @@ -33,37 +32,24 @@ * * @author Ingo Renner */ -class PageFieldMappingIndexer implements SubstitutePageIndexer +class PageFieldMappingIndexer { protected TypoScriptConfiguration $configuration; - protected string $pageIndexingConfigurationName = 'pages'; - public function __construct(TypoScriptConfiguration $configuration = null) - { - $this->configuration = $configuration == null ? Util::getSolrConfiguration() : $configuration; - } - - public function setPageIndexingConfigurationName(string $pageIndexingConfigurationName): void - { - $this->pageIndexingConfigurationName = $pageIndexingConfigurationName; - } - /** - * Returns a substitute document for the currently being indexed page. + * Builds a substitute document for the currently being indexed page. * * Uses the original document and adds fields as defined in * plugin.tx_solr.index.queue.pages.fields. - * - * @param Document $originalPageDocument The original page document. - * - * @return Document A Apache Solr Document object that replace the default page document */ - public function getPageDocument(Document $originalPageDocument): Document + public function __invoke(AfterPageDocumentIsCreatedForIndexingEvent $event): void { - $substitutePageDocument = clone $originalPageDocument; + $substitutePageDocument = clone $event->getDocument(); + $this->configuration = $event->getConfiguration(); + $this->pageIndexingConfigurationName = $event->getIndexingConfigurationName(); - $mappedFields = $this->getMappedFields($originalPageDocument); + $mappedFields = $this->getMappedFields($event->getDocument(), $event->getRecord()); foreach ($mappedFields as $fieldName => $fieldValue) { if (isset($substitutePageDocument->{$fieldName})) { // reset = overwrite, especially important to not make fields @@ -77,19 +63,18 @@ public function getPageDocument(Document $originalPageDocument): Document } } - return $substitutePageDocument; + $event->overrideDocument($substitutePageDocument); } /** * Gets the mapped fields as an array mapping field names to values. * * @param Document $pageDocument The original page document. - * * @return array An array mapping field names to their values. * * @throws InvalidFieldNameException */ - protected function getMappedFields(Document $pageDocument): array + protected function getMappedFields(Document $pageDocument, array $pageRecord): array { $fields = []; @@ -102,7 +87,7 @@ protected function getMappedFields(Document $pageDocument): array 1435441863 ); } - $fields[$mappedFieldName] = $this->resolveFieldValue($mappedFieldName, $pageDocument); + $fields[$mappedFieldName] = $this->resolveFieldValue($mappedFieldName, $pageDocument, $pageRecord); } return $fields; @@ -115,13 +100,10 @@ protected function getMappedFields(Document $pageDocument): array * Otherwise, the plain page record field value is used. * * @param string $solrFieldName The Solr field name to resolve the value from the item's record - * * @return string|array The resolved value to be indexed */ - protected function resolveFieldValue(string $solrFieldName, Document $pageDocument): array|string + protected function resolveFieldValue(string $solrFieldName, Document $pageDocument, array $pageRecord): array|string { - $pageRecord = $GLOBALS['TSFE']->page; - $pageIndexingConfiguration = $this->configuration->getIndexQueueFieldsConfigurationByConfigurationName($this->pageIndexingConfigurationName); if (isset($pageIndexingConfiguration[$solrFieldName . '.'])) { diff --git a/Classes/IndexQueue/FrontendHelper/PageIndexer.php b/Classes/IndexQueue/FrontendHelper/PageIndexer.php index 8475dc516d..27158ed7ae 100644 --- a/Classes/IndexQueue/FrontendHelper/PageIndexer.php +++ b/Classes/IndexQueue/FrontendHelper/PageIndexer.php @@ -17,27 +17,39 @@ use ApacheSolrForTypo3\Solr\Access\Rootline; use ApacheSolrForTypo3\Solr\ConnectionManager; +use ApacheSolrForTypo3\Solr\Domain\Search\ApacheSolrDocument\Builder; +use ApacheSolrForTypo3\Solr\Event\Indexing\AfterPageDocumentIsCreatedForIndexingEvent; +use ApacheSolrForTypo3\Solr\Event\Indexing\BeforeDocumentsAreIndexedEvent; +use ApacheSolrForTypo3\Solr\Event\Indexing\BeforePageDocumentIsProcessedForIndexingEvent; +use ApacheSolrForTypo3\Solr\Exception; +use ApacheSolrForTypo3\Solr\FieldProcessor\Service; use ApacheSolrForTypo3\Solr\IndexQueue\Item; use ApacheSolrForTypo3\Solr\IndexQueue\Queue; use ApacheSolrForTypo3\Solr\NoSolrConnectionFoundException; +use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; use ApacheSolrForTypo3\Solr\System\Logging\DebugWriter; use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager; +use ApacheSolrForTypo3\Solr\System\Solr\Document\Document; use ApacheSolrForTypo3\Solr\System\Solr\SolrConnection; -use ApacheSolrForTypo3\Solr\Typo3PageIndexer; use ApacheSolrForTypo3\Solr\Util; use Doctrine\DBAL\Exception as DBALException; +use Psr\EventDispatcher\EventDispatcherInterface; use Throwable; use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException; +use TYPO3\CMS\Core\Site\Entity\Site; +use TYPO3\CMS\Core\Site\Entity\SiteLanguage; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; +use TYPO3\CMS\Core\Utility\MathUtility; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; use TYPO3\CMS\Frontend\Event\AfterCacheableContentIsGeneratedEvent; use UnexpectedValueException; /** - * Index Queue Page Indexer frontend helper to ask the frontend page indexer to - * index the page. + * Index Queue Page Indexer frontend helper to validate if a page + * should be used by the Index Queue. + * + * Once the FrontendHelper construct is separated, this will be a standalone Indexer. * * @author Ingo Renner */ @@ -49,14 +61,21 @@ class PageIndexer extends AbstractFrontendHelper protected string $action = 'indexPage'; /** - * the page currently being indexed. + * Response data */ - protected TypoScriptFrontendController $page; + protected array $responseData = []; /** - * Response data + * Solr server connection. */ - protected array $responseData = []; + protected ?SolrConnection $solrConnection = null; + + /** + * Documents that have been sent to Solr + */ + protected array $documentsSentToSolr = []; + + protected ?TypoScriptConfiguration $configuration = null; /** * Activates a frontend helper by registering for hooks and other @@ -67,10 +86,6 @@ class PageIndexer extends AbstractFrontendHelper public function activate(): void { $this->isActivated = true; - - // indexes fields defined in plugin.tx_solr.index.queue.pages.fields - $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['Indexer']['indexPageSubstitutePageDocument'][PageFieldMappingIndexer::class] = PageFieldMappingIndexer::class; - $this->registerAuthorizationService(); } @@ -94,7 +109,7 @@ protected function getAccessRootline(): Rootline { $stringAccessRootline = ''; - if ($this->request->getParameter('accessRootline')) { + if ($this->request?->getParameter('accessRootline')) { $stringAccessRootline = $this->request->getParameter('accessRootline'); } @@ -158,35 +173,23 @@ protected function getHighestAuthenticationServicePriority(): int /** * Generates the current page's URL as string. - * Uses the provided GET parameters, page id and language id. + * Uses the provided parameters from TSFE, page id and language id. */ - protected function generatePageUrl(): string + protected function generatePageUrl(TypoScriptFrontendController $controller): string { - if ($this->request->getParameter('overridePageUrl')) { + if ($this->request?->getParameter('overridePageUrl')) { return $this->request->getParameter('overridePageUrl'); } - /** @var ContentObjectRenderer $contentObject */ - $contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class); - - $typolinkConfiguration = [ - 'parameter' => $this->page->id, - 'linkAccessRestrictedPages' => '1', - ]; - - $language = GeneralUtility::_GET('L'); - if (!empty($language)) { - $typolinkConfiguration['additionalParams'] = '&L=' . $language; + $parameter = $controller->page['uid']; + $type = $controller->getPageArguments()->getPageType(); + if ($type && MathUtility::canBeInterpretedAsInteger($type)) { + $parameter .= ',' . $type; } - - $url = $contentObject->typoLink_URL($typolinkConfiguration); - - // clean up - if ($url == '') { - $url = '/'; - } - - return $url; + return $controller->cObj->createUrl([ + 'parameter' => $parameter, + 'linkAccessRestrictedPages' => '1', + ]); } /** @@ -197,14 +200,11 @@ public function __invoke(AfterCacheableContentIsGeneratedEvent $event): void if (!$this->isActivated) { return; } + $this->setupConfiguration(); + $tsfe = $event->getController(); - $this->logger = new SolrLogManager(__CLASS__, GeneralUtility::makeInstance(DebugWriter::class)); - - $this->page = $event->getController(); - $configuration = Util::getSolrConfiguration(); - - $logPageIndexed = $configuration->getLoggingIndexingPageIndexed(); - if (!($this->page->config['config']['index_enable'] ?? false)) { + $logPageIndexed = $this->configuration->getLoggingIndexingPageIndexed(); + if (!($tsfe->config['config']['index_enable'] ?? false)) { if ($logPageIndexed) { $this->logger->log( SolrLogManager::ERROR, @@ -216,38 +216,16 @@ public function __invoke(AfterCacheableContentIsGeneratedEvent $event): void try { $indexQueueItem = $this->getIndexQueueItem(); - if (is_null($indexQueueItem)) { + if ($indexQueueItem === null) { throw new UnexpectedValueException('Can not get index queue item', 1482162337); } - - $solrConnection = $this->getSolrConnection($indexQueueItem); - - /** @var Typo3PageIndexer $indexer */ - $indexer = GeneralUtility::makeInstance(Typo3PageIndexer::class, $this->page); - $indexer->setSolrConnection($solrConnection); - $indexer->setPageAccessRootline($this->getAccessRootline()); - $indexer->setPageUrl($this->generatePageUrl()); - $indexer->setMountPointParameter($GLOBALS['TSFE']->MP); - $indexer->setIndexQueueItem($indexQueueItem); - - $this->responseData['pageIndexed'] = (int)$indexer->indexPage(); - $this->responseData['originalPageDocument'] = (array)$indexer->getPageSolrDocument(); - $this->responseData['solrConnection'] = [ - 'rootPage' => $indexQueueItem->getRootPageUid(), - 'sys_language_uid' => Util::getLanguageUid(), - 'solr' => (string)$solrConnection->getNode('write'), - ]; - - $documentsSentToSolr = $indexer->getDocumentsSentToSolr(); - foreach ($documentsSentToSolr as $document) { - $this->responseData['documentsSentToSolr'][] = (array)$document; - } + $this->index($indexQueueItem, $tsfe); } catch (Throwable $e) { $this->responseData['pageIndexed'] = false; - if ($configuration->getLoggingExceptions()) { + if ($this->configuration->getLoggingExceptions()) { $this->logger->log( SolrLogManager::ERROR, - 'Exception while trying to index page ' . $this->page->id, + 'Exception while trying to index page ' . $tsfe->id, [ $e->__toString(), ] @@ -267,23 +245,66 @@ public function __invoke(AfterCacheableContentIsGeneratedEvent $event): void } } + /** + * @internal currently only public for tests + */ + public function index(Item $indexQueueItem, TypoScriptFrontendController $tsfe): void + { + $this->solrConnection = $this->getSolrConnection($indexQueueItem, $tsfe->getLanguage(), $this->configuration->getLoggingExceptions()); + + $document = $this->getPageDocument($tsfe, $this->generatePageUrl($tsfe), $this->getAccessRootline(), $tsfe->MP); + $document = $this->substitutePageDocument($document, $tsfe->getSite(), $tsfe->getLanguage(), $tsfe->page, $indexQueueItem); + + $this->responseData['pageIndexed'] = (int)$this->indexPage($document, $indexQueueItem, $tsfe->getSite(), $tsfe->getLanguage()); + $this->responseData['originalPageDocument'] = (array)$document; + $this->responseData['solrConnection'] = [ + 'rootPage' => $indexQueueItem->getRootPageUid(), + 'sys_language_uid' => $tsfe->getLanguage()->getLanguageId(), + 'solr' => (string)$this->solrConnection->getNode('write'), + ]; + + foreach ($this->documentsSentToSolr as $document) { + $this->responseData['documentsSentToSolr'][] = (array)$document; + } + } + /** * Gets the solr connection to use for indexing the page based on the * Index Queue item's properties. * - * @throws AspectNotFoundException * @throws NoSolrConnectionFoundException * @throws DBALException */ - protected function getSolrConnection(Item $indexQueueItem): SolrConnection + protected function getSolrConnection(Item $indexQueueItem, SiteLanguage $siteLanguage, bool $logExceptions): SolrConnection { - /** @var ConnectionManager $connectionManager */ $connectionManager = GeneralUtility::makeInstance(ConnectionManager::class); + try { + $solrConnection = $connectionManager->getConnectionByRootPageId($indexQueueItem->getRootPageUid(), $siteLanguage->getLanguageId()); + if (!$solrConnection->getWriteService()->ping()) { + throw new Exception( + 'Could not connect to Solr server.', + 1323946472 + ); + } + return $solrConnection; + } catch (Throwable $e) { + $this->logger->log( + SolrLogManager::ERROR, + $e->getMessage() . ' Error code: ' . $e->getCode() + ); - return $connectionManager->getConnectionByRootPageId( - $indexQueueItem->getRootPageUid(), - Util::getLanguageUid() - ); + // TODO extract to a class "ExceptionLogger" + if ($logExceptions) { + $this->logger->log( + SolrLogManager::ERROR, + 'Exception while trying to index a page', + [ + $e->__toString(), + ] + ); + } + throw $e; + } } /** @@ -293,8 +314,132 @@ protected function getSolrConnection(Item $indexQueueItem): SolrConnection */ protected function getIndexQueueItem(): ?Item { - /** @var Queue $indexQueue */ $indexQueue = GeneralUtility::makeInstance(Queue::class); return $indexQueue->getItem($this->request->getParameter('item')); } + + /** + * Allows third party extensions to replace or modify the page document + * created by this indexer. + * + * @param Document $pageDocument The page document created by this indexer. + * @return Document An Apache Solr document representing the currently indexed page + */ + protected function substitutePageDocument(Document $pageDocument, Site $site, SiteLanguage $siteLanguage, array $pageRecord, Item $indexQueueItem): Document + { + $event = new AfterPageDocumentIsCreatedForIndexingEvent($pageDocument, $indexQueueItem, $site, $siteLanguage, $pageRecord, $this->configuration); + $event = $this->getEventDispatcher()->dispatch($event); + return $event->getDocument(); + } + + /** + * Builds the Solr document for the current page. + * + * @return Document A document representing the page + * + * @throws AspectNotFoundException + * @throws DBALException + */ + protected function getPageDocument(TypoScriptFrontendController $tsfe, string $url, Rootline $pageAccessRootline, string $mountPointParameter): Document + { + $documentBuilder = GeneralUtility::makeInstance(Builder::class); + return $documentBuilder->fromPage($tsfe, $url, $pageAccessRootline, $mountPointParameter); + } + + /** + * Indexes a page. + * + * @return bool TRUE after successfully indexing the page, FALSE on error + * + * @throws AspectNotFoundException + * @throws DBALException + * @throws Exception + */ + public function indexPage(Document $pageDocument, Item $indexQueueItem, Site $site, SiteLanguage $siteLanguage): bool + { + $event = new BeforePageDocumentIsProcessedForIndexingEvent($pageDocument, $site, $siteLanguage, $indexQueueItem); + $event = $this->getEventDispatcher()->dispatch($event); + $documents = $event->getDocuments(); + + $this->processDocuments($documents); + + $event = new BeforeDocumentsAreIndexedEvent($pageDocument, $site, $siteLanguage, $indexQueueItem, $documents); + $event = $this->getEventDispatcher()->dispatch($event); + $documents = $event->getDocuments(); + + $pageIndexed = $this->addDocumentsToSolrIndex($documents); + $this->documentsSentToSolr = $documents; + + return $pageIndexed; + } + + /** + * Sends the given documents to the field processing service which takes + * care of manipulating fields as defined in the field's configuration. + * + * @param Document[] $documents An array of documents to manipulate + * @throws DBALException + * @throws Exception + */ + protected function processDocuments(array $documents): void + { + $processingInstructions = $this->configuration?->getIndexFieldProcessingInstructionsConfiguration(); + if (is_array($processingInstructions) && !empty($processingInstructions)) { + $service = GeneralUtility::makeInstance(Service::class); + $service->processDocuments($documents, $processingInstructions); + } + } + + /** + * Adds the collected documents to the Solr index. + * + * @param Document[] $documents An array of Document objects. + * @return bool TRUE if documents were added successfully, FALSE otherwise + */ + protected function addDocumentsToSolrIndex(array $documents): bool + { + $documentsAdded = false; + + if ($documents === []) { + return false; + } + + try { + $this->logger->log(SolrLogManager::INFO, 'Adding ' . count($documents) . ' documents.', $documents); + + // chunk adds by 20 + $documentChunks = array_chunk($documents, 20); + foreach ($documentChunks as $documentChunk) { + $response = $this->solrConnection->getWriteService()->addDocuments($documentChunk); + if ($response->getHttpStatus() != 200) { + throw new \RuntimeException('Solr Request failed.', 1331834983); + } + } + + $documentsAdded = true; + } catch (Throwable $e) { + $this->logger->log(SolrLogManager::ERROR, $e->getMessage() . ' Error code: ' . $e->getCode()); + + if ($this->configuration->getLoggingExceptions()) { + $this->logger->log(SolrLogManager::ERROR, 'Exception while adding documents', [$e->__toString()]); + } + } + + return $documentsAdded; + } + + /** + * @internal only used for tests + */ + public function setupConfiguration(): void + { + // currently needed for tests, will be separated. + $this->logger = new SolrLogManager(__CLASS__, GeneralUtility::makeInstance(DebugWriter::class)); + $this->configuration = Util::getSolrConfiguration(); + } + + protected function getEventDispatcher(): EventDispatcherInterface + { + return GeneralUtility::makeInstance(EventDispatcherInterface::class); + } } diff --git a/Classes/IndexQueue/InitializationPostProcessor.php b/Classes/IndexQueue/InitializationPostProcessor.php deleted file mode 100644 index bd079d2b6f..0000000000 --- a/Classes/IndexQueue/InitializationPostProcessor.php +++ /dev/null @@ -1,39 +0,0 @@ - - */ -interface InitializationPostProcessor -{ - /** - * Post process Index Queue initialization - * - * @param Site $site The site to initialize - * @param array $indexingConfigurations Initialized indexing configurations - * @param array $initializationStatus Results of Index Queue initializations - */ - public function postProcessIndexQueueInitialization( - Site $site, - array $indexingConfigurations, - array $initializationStatus - ); -} diff --git a/Classes/IndexQueue/PageIndexer.php b/Classes/IndexQueue/PageIndexer.php index d55e6ab29b..574e31949d 100644 --- a/Classes/IndexQueue/PageIndexer.php +++ b/Classes/IndexQueue/PageIndexer.php @@ -19,14 +19,13 @@ use ApacheSolrForTypo3\Solr\Access\Rootline; use ApacheSolrForTypo3\Solr\Access\RootlineElement; -use ApacheSolrForTypo3\Solr\Domain\Index\PageIndexer\Helper\UriBuilder\AbstractUriStrategy; -use ApacheSolrForTypo3\Solr\Domain\Index\PageIndexer\Helper\UriStrategyFactory; +use ApacheSolrForTypo3\Solr\Domain\Index\PageIndexer\PageUriBuilder; use ApacheSolrForTypo3\Solr\NoSolrConnectionFoundException; use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager; use ApacheSolrForTypo3\Solr\System\Solr\SolrConnection; +use ApacheSolrForTypo3\Solr\System\Util\SiteUtility; use Doctrine\DBAL\Exception as DBALException; use Exception; -use RuntimeException; use TYPO3\CMS\Core\Type\Bitmask\PageTranslationVisibility; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -235,24 +234,28 @@ protected function getPageIndexerRequest(): PageIndexerRequest * and then actually build and return the page URL. * * @throws DBALException - * @throws Exception + * @throws \Exception */ protected function getDataUrl(Item $item, int $language = 0): string { $pageId = $item->getRecordUid(); - $strategy = $this->getUriStrategy($pageId); + $uriBuilder = $this->getUriBuilder($pageId); $mountPointParameter = $this->getMountPageDataUrlParameter($item); - return $strategy->getPageIndexingUriFromPageItemAndLanguageId($item, $language, $mountPointParameter, $this->options); + return $uriBuilder->getPageIndexingUriFromPageItemAndLanguageId($item, $language, $mountPointParameter, $this->options); } /** * Returns the URI strategy object * - * @throws Exception + * @throws \Exception */ - protected function getUriStrategy(int $pageId): AbstractUriStrategy + protected function getUriBuilder(int $pageId): PageUriBuilder { - return GeneralUtility::makeInstance(UriStrategyFactory::class)->getForPageId($pageId); + if (!SiteUtility::getIsSiteManagedSite($pageId)) { + throw new \Exception('Site of page with uid ' . $pageId . ' is not a TYPO3 managed site'); + } + + return GeneralUtility::makeInstance(PageUriBuilder::class); } /** @@ -329,7 +332,7 @@ protected function indexPage(Item $item, ?int $language = 0, ?int $userGroup = 0 if (empty($indexActionResult['pageIndexed'])) { $message = 'Failed indexing page Index Queue item: ' . $item->getIndexQueueUid() . ' url: ' . $indexRequestUrl; - throw new RuntimeException($message, 1331837081); + throw new \RuntimeException($message, 1331837081); } return $response; diff --git a/Classes/SubstitutePageIndexer.php b/Classes/SubstitutePageIndexer.php deleted file mode 100644 index f8cb4c8a82..0000000000 --- a/Classes/SubstitutePageIndexer.php +++ /dev/null @@ -1,37 +0,0 @@ - - */ -interface SubstitutePageIndexer -{ - /** - * returns a substitute document for the currently being indexed page - * - * @param Document $originalPageDocument The original page document. - * @return Document returns an \ApacheSolrForTypo3\Solr\System\Solr\Document\Document object that replace the default page document - */ - public function getPageDocument(Document $originalPageDocument): Document; -} diff --git a/Classes/System/Solr/Node.php b/Classes/System/Solr/Node.php deleted file mode 100644 index e1b039e73f..0000000000 --- a/Classes/System/Solr/Node.php +++ /dev/null @@ -1,144 +0,0 @@ - - * @copyright Copyright (c) 2009-2020 Timo Hund - * - * @deprecated Class will be removed with Ext:solr 12.x. Use class \Solarium\Core\Client\Endpoint instead. - */ -class Node extends Endpoint -{ - /** - * Node constructor. - */ - public function __construct( - string $scheme = 'http', - string $host = 'localhost', - int $port = 8983, - string $path = '/solr/core_en/', - ?string $username = null, - ?string $password = null, - ) { - $elements = explode('/', trim($path, '/')); - $coreName = (string)array_pop($elements); - // Remove API version - array_pop($elements); - - // The path should always have the same format! - $path = trim(implode('/', $elements), '/'); - - $options = [ - 'scheme' => $scheme, - 'host' => $host, - 'port' => $port, - 'path' => '/' . $path, - 'collection' => null, - 'core' => $coreName, - 'leader' => false, - ]; - - parent::__construct($options); - $this->setAuthentication($username, $password); - } - - public static function fromArray(array $configuration): Node - { - static::checkIfRequiredKeyIsSet($configuration, 'scheme'); - static::checkIfRequiredKeyIsSet($configuration, 'host'); - static::checkIfRequiredKeyIsSet($configuration, 'port'); - static::checkIfRequiredKeyIsSet($configuration, 'path'); - - $scheme = $configuration['scheme']; - $host = $configuration['host']; - $port = (int)$configuration['port']; - $path = $configuration['path']; - - $username = $configuration['username'] ?? ''; - $password = $configuration['password'] ?? ''; - return new Node($scheme, $host, $port, $path, $username, $password); - } - - /** - * Checks if the required configuration option is set. - * - * @throws UnexpectedValueException - */ - protected static function checkIfRequiredKeyIsSet(array $configuration, string $name): void - { - if (empty($configuration[$name])) { - throw new UnexpectedValueException('Required solr connection property ' . $name . ' is missing.'); - } - } - - public function getUsername(): string - { - return (string)$this->getOption('username'); - } - - public function getPassword(): string - { - return (string)$this->getOption('password'); - } - - /** - * Returns the path including api path. - */ - public function getCoreBasePath(): string - { - $pathWithoutLeadingAndTrailingSlashes = trim(trim($this->getPath()), '/'); - $pathWithoutLastSegment = substr($pathWithoutLeadingAndTrailingSlashes, 0, strrpos($pathWithoutLeadingAndTrailingSlashes, '/')); - return ($pathWithoutLastSegment === '') ? '/' : '/' . $pathWithoutLastSegment . '/'; - } - - /** - * Returns the core name from the configured path. - * - * @deprecated Will be removed with Ext:solr 12.x. Use method getCore() instead. - */ - public function getCoreName(): string - { - return $this->getCore(); - } - - public function getSolariumClientOptions(): array - { - return [ - 'host' => $this->getHost(), - 'port' => $this->getPort(), - 'scheme' => $this->getScheme(), - 'path' => $this->getPath(), - 'core' => $this->getCore(), - ]; - } - - /** - * @deprecated Will be removed with Ext:solr 12.x. Use methods getCoreBaseUri() for API version 1 instead - */ - public function __toString(): string - { - return $this->getCoreBaseUri(); - } -} diff --git a/Classes/System/Solr/SolrConnection.php b/Classes/System/Solr/SolrConnection.php index 33944a7ebe..3d800610f1 100644 --- a/Classes/System/Solr/SolrConnection.php +++ b/Classes/System/Solr/SolrConnection.php @@ -34,6 +34,7 @@ use Psr\Http\Message\StreamFactoryInterface; use Solarium\Client; use Solarium\Core\Client\Adapter\Psr18Adapter; +use Solarium\Core\Client\Endpoint; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -58,7 +59,7 @@ class SolrConnection protected ?SchemaParser $schemaParser = null; /** - * @var Node[] + * @var Endpoint[] */ protected array $nodes = []; @@ -83,8 +84,8 @@ class SolrConnection * @throws NotFoundExceptionInterface */ public function __construct( - Node $readNode, - Node $writeNode, + Endpoint $readNode, + Endpoint $writeNode, TypoScriptConfiguration $configuration = null, SynonymParser $synonymParser = null, StopWordParser $stopWordParser = null, @@ -112,7 +113,7 @@ public function __construct( /** * Returns Endpoint by key */ - public function getNode(string $key): Node + public function getNode(string $key): Endpoint { return $this->nodes[$key]; } @@ -200,12 +201,12 @@ protected function buildWriteService(): SolrWriteService */ protected function initializeClient(Client $client, string $endpointKey): Client { - if (trim($this->getNode($endpointKey)->getUsername()) === '') { + if (trim($this->getNode($endpointKey)->getOption('username')) === '') { return $client; } - $username = $this->getNode($endpointKey)->getUsername(); - $password = $this->getNode($endpointKey)->getPassword(); + $username = $this->getNode($endpointKey)->getOption('username'); + $password = $this->getNode($endpointKey)->getOption('password'); $this->setAuthenticationOnAllEndpoints($client, $username, $password); return $client; @@ -236,7 +237,7 @@ protected function getClient(string $endpointKey): Client $client->getPlugin('postbigrequest'); $client->clearEndpoints(); - $newEndpointOptions = $this->getNode($endpointKey)->getSolariumClientOptions(); + $newEndpointOptions = $this->getNode($endpointKey)->getOptions(); $newEndpointOptions['key'] = $endpointKey; $client->createEndpoint($newEndpointOptions, true); diff --git a/Classes/Task/EventQueueWorkerTaskAdditionalFieldProvider.php b/Classes/Task/EventQueueWorkerTaskAdditionalFieldProvider.php index e58fdd21db..c66a33e4f3 100644 --- a/Classes/Task/EventQueueWorkerTaskAdditionalFieldProvider.php +++ b/Classes/Task/EventQueueWorkerTaskAdditionalFieldProvider.php @@ -59,8 +59,6 @@ public function getAdditionalFields( $additionalFields['limit'] = [ 'code' => '', 'label' => 'LLL:EXT:solr/Resources/Private/Language/locallang_be.xlf:task.eventQueueWorkerTask.limit', - 'cshKey' => '', - 'cshLabel' => '', ]; return $additionalFields; diff --git a/Classes/Task/IndexQueueWorkerTaskAdditionalFieldProvider.php b/Classes/Task/IndexQueueWorkerTaskAdditionalFieldProvider.php index abb32594ef..90438678e0 100644 --- a/Classes/Task/IndexQueueWorkerTaskAdditionalFieldProvider.php +++ b/Classes/Task/IndexQueueWorkerTaskAdditionalFieldProvider.php @@ -90,22 +90,16 @@ public function getAdditionalFields( $taskInfo['site'] ), 'label' => 'LLL:EXT:solr/Resources/Private/Language/locallang.xlf:field_site', - 'cshKey' => '', - 'cshLabel' => '', ]; $additionalFields['documentsToIndexLimit'] = [ 'code' => '', 'label' => 'LLL:EXT:solr/Resources/Private/Language/locallang.xlf:indexqueueworker_field_documentsToIndexLimit', - 'cshKey' => '', - 'cshLabel' => '', ]; $additionalFields['forcedWebRoot'] = [ 'code' => '', 'label' => 'LLL:EXT:solr/Resources/Private/Language/locallang.xlf:indexqueueworker_field_forcedWebRoot', - 'cshKey' => '', - 'cshLabel' => '', ]; return $additionalFields; diff --git a/Classes/Task/OptimizeIndexTaskAdditionalFieldProvider.php b/Classes/Task/OptimizeIndexTaskAdditionalFieldProvider.php index a7317a08a7..85cee2029e 100644 --- a/Classes/Task/OptimizeIndexTaskAdditionalFieldProvider.php +++ b/Classes/Task/OptimizeIndexTaskAdditionalFieldProvider.php @@ -136,15 +136,11 @@ public function getAdditionalFields( $additionalFields['site'] = [ 'code' => $siteSelectorField->getAvailableSitesSelector('tx_scheduler[site]', $this->site), 'label' => $this->languageFile . ':field_site', - 'cshKey' => '', - 'cshLabel' => '', ]; $additionalFields['cores'] = [ 'code' => $this->getCoreSelectorMarkup(), 'label' => $this->languageFile . ':field_cores', - 'cshKey' => '', - 'cshLabel' => '', ]; return $additionalFields; diff --git a/Classes/Task/ReIndexTaskAdditionalFieldProvider.php b/Classes/Task/ReIndexTaskAdditionalFieldProvider.php index 1c823bc63e..b6ec26f533 100644 --- a/Classes/Task/ReIndexTaskAdditionalFieldProvider.php +++ b/Classes/Task/ReIndexTaskAdditionalFieldProvider.php @@ -123,15 +123,11 @@ public function getAdditionalFields( $this->site ), 'label' => 'LLL:EXT:solr/Resources/Private/Language/locallang.xlf:field_site', - 'cshKey' => '', - 'cshLabel' => '', ]; $additionalFields['indexingConfigurations'] = [ 'code' => $this->getIndexingConfigurationSelector(), 'label' => 'Index Queue configurations to re-index', - 'cshKey' => '', - 'cshLabel' => '', ]; return $additionalFields; diff --git a/Classes/Typo3PageIndexer.php b/Classes/Typo3PageIndexer.php deleted file mode 100644 index 1e2e0c557e..0000000000 --- a/Classes/Typo3PageIndexer.php +++ /dev/null @@ -1,423 +0,0 @@ - - * @author Daniel Poetzinger - * @author Timo Schmidt - */ -class Typo3PageIndexer -{ - /** - * ID of the current page's Solr document. - */ - protected static string $pageSolrDocumentId = ''; - - /** - * The Solr document generated for the current page. - */ - protected static Document $pageSolrDocument; - - /** - * The mount point parameter used in the Frontend controller. - */ - protected string $mountPointParameter = ''; - - /** - * Solr server connection. - */ - protected ?SolrConnection $solrConnection = null; - - /** - * Frontend page object (TSFE). - */ - protected TypoScriptFrontendController $page; - - /** - * URL to be indexed as the page's URL - */ - protected string $pageUrl = ''; - - /** - * The page's access rootline - */ - protected Rootline $pageAccessRootline; - - /** - * Documents that have been sent to Solr - */ - protected array $documentsSentToSolr = []; - - protected TypoScriptConfiguration $configuration; - - protected Item $indexQueueItem; - - protected SolrLogManager $logger; - - /** - * Constructor - * - * @param TypoScriptFrontendController $page The page to index - */ - public function __construct(TypoScriptFrontendController $page) - { - $this->logger = new SolrLogManager(__CLASS__, GeneralUtility::makeInstance(DebugWriter::class)); - - $this->page = $page; - $this->pageUrl = GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'); - $this->configuration = Util::getSolrConfiguration(); - - try { - $this->initializeSolrConnection(); - } catch (Throwable $e) { - $this->logger->log( - SolrLogManager::ERROR, - $e->getMessage() . ' Error code: ' . $e->getCode() - ); - - // TODO extract to a class "ExceptionLogger" - if ($this->configuration->getLoggingExceptions()) { - $this->logger->log( - SolrLogManager::ERROR, - 'Exception while trying to index a page', - [ - $e->__toString(), - ] - ); - } - } - - $this->pageAccessRootline = GeneralUtility::makeInstance(Rootline::class, ''); - } - - public function setIndexQueueItem(Item $indexQueueItem): void - { - $this->indexQueueItem = $indexQueueItem; - } - - /** - * Initializes the Solr server connection. - * - * @throws AspectNotFoundException - * @throws DBALException - * @throws Exception - * @throws NoSolrConnectionFoundException - */ - protected function initializeSolrConnection(): void - { - $solr = GeneralUtility::makeInstance(ConnectionManager::class)->getConnectionByPageId($this->page->id, Util::getLanguageUid()); - - // do not continue if no server is available - if (!$solr->getWriteService()->ping()) { - throw new Exception( - 'No Solr instance available while trying to index a page.', - 1234790825 - ); - } - - $this->solrConnection = $solr; - } - - /** - * Gets the current page's Solr document ID. - * - * @return string The page's Solr document ID or empty string in case no document was generated yet. - */ - public static function getPageSolrDocumentId(): string - { - return self::$pageSolrDocumentId; - } - - /** - * Gets the Solr document generated for the current page. - * - * @return Document|null The page's Solr document or NULL if it has not been generated yet. - */ - public static function getPageSolrDocument(): ?Document - { - return self::$pageSolrDocument; - } - - /** - * Allows to provide a Solr server connection other than the one - * initialized by the constructor. - * - * @param SolrConnection $solrConnection Solr connection - * @throws Exception if the Solr server cannot be reached - */ - public function setSolrConnection(SolrConnection $solrConnection): void - { - if (!$solrConnection->getWriteService()->ping()) { - throw new Exception( - 'Could not connect to Solr server.', - 1323946472 - ); - } - - $this->solrConnection = $solrConnection; - } - - /** - * Indexes a page. - * - * @return bool TRUE after successfully indexing the page, FALSE on error - * - * @throws AspectNotFoundException - * @throws DBALException - * @throws Exception - */ - public function indexPage(): bool - { - if (is_null($this->solrConnection)) { - // intended early return as it doesn't make sense to continue - // and waste processing time if the solr server isn't available - // anyways - // FIXME use an exception - return false; - } - - $pageDocument = $this->getPageDocument(); - $pageDocument = $this->substitutePageDocument($pageDocument); - - self::$pageSolrDocument = $pageDocument; - $event = new BeforePageDocumentIsProcessedForIndexingEvent($pageDocument, $this->page->getSite(), $this->page->getLanguage(), $this->indexQueueItem); - $event = $this->getEventDispatcher()->dispatch($event); - $documents = $event->getDocuments(); - - $this->processDocuments($documents); - - $event = new BeforeDocumentsAreIndexedEvent($pageDocument, $this->page->getSite(), $this->page->getLanguage(), $this->indexQueueItem, $documents); - $event = $this->getEventDispatcher()->dispatch($event); - $documents = $event->getDocuments(); - - $pageIndexed = $this->addDocumentsToSolrIndex($documents); - $this->documentsSentToSolr = $documents; - - return $pageIndexed; - } - - /** - * Builds the Solr document for the current page. - * - * @return Document A document representing the page - * - * @throws AspectNotFoundException - * @throws DBALException - */ - protected function getPageDocument(): Document - { - $documentBuilder = GeneralUtility::makeInstance(Builder::class); - $document = $documentBuilder->fromPage($this->page, $this->pageUrl, $this->pageAccessRootline, $this->mountPointParameter); - - self::$pageSolrDocumentId = $document['id']; - - return $document; - } - - /** - * Gets the mount point parameter that is used in the Frontend controller. - */ - public function getMountPointParameter(): string - { - return $this->mountPointParameter; - } - - // Misc - - /** - * Sets the mount point parameter that is used in the Frontend controller. - */ - public function setMountPointParameter(string $mountPointParameter): void - { - $this->mountPointParameter = $mountPointParameter; - } - - /** - * Allows third party extensions to replace or modify the page document - * created by this indexer. - * - * @param Document $pageDocument The page document created by this indexer. - * @return Document An Apache Solr document representing the currently indexed page - */ - protected function substitutePageDocument(Document $pageDocument): Document - { - if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['Indexer']['indexPageSubstitutePageDocument'] ?? null)) { - return $pageDocument; - } - - $indexConfigurationName = $this->getIndexConfigurationNameForCurrentPage(); - foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['Indexer']['indexPageSubstitutePageDocument'] as $classReference) { - $substituteIndexer = GeneralUtility::makeInstance($classReference); - - if (!$substituteIndexer instanceof SubstitutePageIndexer) { - $message = get_class($substituteIndexer) . ' must implement interface ' . SubstitutePageIndexer::class; - throw new UnexpectedValueException($message, 1310491001); - } - - if ($substituteIndexer instanceof PageFieldMappingIndexer) { - $substituteIndexer->setPageIndexingConfigurationName($indexConfigurationName); - } - - $pageDocument = $substituteIndexer->getPageDocument($pageDocument); - } - - return $pageDocument; - } - - /** - * Retrieves the indexConfigurationName from the related queueItem, or falls back to pages when no queue item set. - */ - protected function getIndexConfigurationNameForCurrentPage(): string - { - return isset($this->indexQueueItem) ? $this->indexQueueItem->getIndexingConfigurationName() : 'pages'; - } - - /** - * Sends the given documents to the field processing service which takes - * care of manipulating fields as defined in the field's configuration. - * - * @param Document[] $documents An array of documents to manipulate - * @throws DBALException - * @throws Exception - */ - protected function processDocuments(array $documents): void - { - $processingInstructions = $this->configuration->getIndexFieldProcessingInstructionsConfiguration(); - if (count($processingInstructions) > 0) { - $service = GeneralUtility::makeInstance(Service::class); - $service->processDocuments($documents, $processingInstructions); - } - } - - /** - * Adds the collected documents to the Solr index. - * - * @param Document[] $documents An array of Document objects. - * @return bool TRUE if documents were added successfully, FALSE otherwise - */ - protected function addDocumentsToSolrIndex(array $documents): bool - { - $documentsAdded = false; - - if (!count($documents)) { - return false; - } - - try { - $this->logger->log(SolrLogManager::INFO, 'Adding ' . count($documents) . ' documents.', $documents); - - // chunk adds by 20 - $documentChunks = array_chunk($documents, 20); - foreach ($documentChunks as $documentChunk) { - $response = $this->solrConnection->getWriteService()->addDocuments($documentChunk); - if ($response->getHttpStatus() != 200) { - throw new RuntimeException('Solr Request failed.', 1331834983); - } - } - - $documentsAdded = true; - } catch (Throwable $e) { - $this->logger->log(SolrLogManager::ERROR, $e->getMessage() . ' Error code: ' . $e->getCode()); - - if ($this->configuration->getLoggingExceptions()) { - $this->logger->log(SolrLogManager::ERROR, 'Exception while adding documents', [$e->__toString()]); - } - } - - return $documentsAdded; - } - - /** - * Gets the current page's URL. - * - * @return string URL of the current page. - */ - public function getPageUrl(): string - { - return $this->pageUrl; - } - - /** - * Sets the URL to use for the page document. - * - * @param string $url The page's URL. - */ - public function setPageUrl(string $url): void - { - $this->pageUrl = $url; - } - - /** - * Gets the page's access rootline. - * - * @return Rootline The page's access rootline - */ - public function getPageAccessRootline(): Rootline - { - return $this->pageAccessRootline; - } - - /** - * Sets the page's access rootline. - * - * @param Rootline $accessRootline The page's access rootline - */ - public function setPageAccessRootline(Rootline $accessRootline): void - { - $this->pageAccessRootline = $accessRootline; - } - - /** - * Gets the documents that have been sent to Solr - * - * @return Document[] An array of Document objects - */ - public function getDocumentsSentToSolr(): array - { - return $this->documentsSentToSolr; - } - - protected function getEventDispatcher(): EventDispatcherInterface - { - return GeneralUtility::makeInstance(EventDispatcherInterface::class); - } -} diff --git a/Classes/ViewHelpers/Backend/Security/IfHasAccessToModuleViewHelper.php b/Classes/ViewHelpers/Backend/Security/IfHasAccessToModuleViewHelper.php index 3fe8958759..7ac72d3721 100644 --- a/Classes/ViewHelpers/Backend/Security/IfHasAccessToModuleViewHelper.php +++ b/Classes/ViewHelpers/Backend/Security/IfHasAccessToModuleViewHelper.php @@ -19,7 +19,7 @@ use InvalidArgumentException; use RuntimeException; -use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use TYPO3\CMS\Backend\Module\ModuleProvider; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper; @@ -61,24 +61,16 @@ public function initializeArguments(): void */ protected static function evaluateCondition($arguments = null) { - /** @var BackendUserAuthentication $beUser */ - $beUser = $GLOBALS['BE_USER']; try { - $hasAccessToModule = $beUser->modAccess( - self::getModuleConfiguration(self::getModuleSignatureFromArguments($arguments)) + $hasAccessToModule = self::getModuleProvider()->accessGranted( + self::getModuleSignatureFromArguments($arguments), + $GLOBALS['BE_USER'] ); } catch (RuntimeException $exception) { return false; } - return $hasAccessToModule; - } - /** - * Returns the backend module configuration - */ - protected static function getModuleConfiguration(string $moduleSignature) - { - return $GLOBALS['TBE_MODULES']['_configuration'][$moduleSignature]; + return $hasAccessToModule; } /** @@ -92,14 +84,18 @@ protected static function getModuleSignatureFromArguments(array $arguments): str $possibleErrorCode = 1496311009; if (!is_string($moduleSignature)) { $moduleSignature = $arguments['main']; - $subModuleName = $arguments['extension'] . GeneralUtility::underscoredToUpperCamelCase($arguments['sub']); + $subModuleName = GeneralUtility::underscoredToUpperCamelCase($arguments['sub']); $moduleSignature .= '_' . $subModuleName; $possibleErrorMessageAppendix = vsprintf(self::ERROR_APPENDIX_FOR_SIGNATURE_RESOLUTION, [$arguments['extension'], $arguments['main'], $arguments['sub']]); $possibleErrorCode = 1496311010; } - if (!isset($GLOBALS['TBE_MODULES']['_configuration'][$moduleSignature])) { + + $moduleSignature = strtolower($moduleSignature); + + if (!self::getModuleProvider()->isModuleRegistered($moduleSignature)) { throw new RuntimeException(vsprintf('Module with signature "%s" is not configured or couldn\'t be resolved. ' . $possibleErrorMessageAppendix, [$moduleSignature]), $possibleErrorCode); } + return $moduleSignature; } @@ -120,4 +116,9 @@ public function validateArguments(): void throw new InvalidArgumentException('ifHasAccessToModule view helper requires either "signature" or all three other arguments: "extension", "main" and "sub". Please set arguments properly.', 1496314352); } } + + protected static function getModuleProvider(): ModuleProvider + { + return GeneralUtility::makeInstance(ModuleProvider::class); + } } diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index b02292632b..69458115d2 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -195,15 +195,28 @@ services: ### Indexing ApacheSolrForTypo3\Solr\EventListener\PageIndexer\FrontendGroupsModifier: + autowire: true tags: - name: event.listener identifier: 'solr.index.PageIndexer.FrontendUserAuthenticator' - event: TYPO3\CMS\Frontend\Authentication\ModifyResolvedFrontendGroupsEvent + ApacheSolrForTypo3\Solr\IndexQueue\FrontendHelper\PageIndexer: + autowire: true tags: - name: event.listener identifier: 'solr.index.FrontendHelper.PageIndexer.indexPageContentAfterCacheableContentIsGenerated' - event: TYPO3\CMS\Frontend\Event\AfterCacheableContentIsGeneratedEvent + + ApacheSolrForTypo3\Solr\EventListener\PageIndexer\AdditionalFieldsForPageIndexing: + autowire: true + tags: + - name: event.listener + identifier: 'solr.index.AdditionalFieldsForPageIndexing' + + ApacheSolrForTypo3\Solr\IndexQueue\FrontendHelper\PageFieldMappingIndexer: + autowire: true + tags: + - name: event.listener + identifier: 'solr.index.PageFieldMappingIndexer' ### EXT:solr content objects ApacheSolrForTypo3\Solr\ContentObject\Classification: diff --git a/Documentation/Development/Indexing.rst b/Documentation/Development/Indexing.rst index 05016731d8..66bc304b27 100644 --- a/Documentation/Development/Indexing.rst +++ b/Documentation/Development/Indexing.rst @@ -4,12 +4,12 @@ Indexing ======== -In this section i describe the possibilities to extend page indexing in EXT:solr with custom code via TYPO3 Hooks or PSR-14 events. +This section describes the possibilities to extend page indexing in EXT:solr with custom code via PSR-14 events. Page Indexing ============= -There are several points to extend the Typo3PageIndexer class and register own classes that are used during the indexing. +There are several points to extend the Page Indexer class and register own classes that are used during the indexing. BeforePageDocumentIsProcessedForIndexingEvent --------------------------------------------- @@ -43,14 +43,34 @@ The corresponding event listener class: For other records than pages, the PSR-14 Event :php:class:`ApacheSolrForTypo3\Solr\Event\Indexing\BeforeDocumentIsProcessedForIndexingEvent` can be used. -indexPageSubstitutePageDocument -------------------------------- +AfterPageDocumentIsCreatedForIndexingEvent +------------------------------------------ -Registered classes can be used to replace/substitute a Solr document of a page. +Registered event listeners can be used to replace/substitute a Solr document of a page. +Registration of an event listener in your extension's :file:`Services.yaml`: + +.. code-block:: yaml + + MyVendor\MyPackage\EventListeners\MyEventListener: + tags: + - name: event.listener + identifier: 'my-package/modify-page' + +The corresponding event listener class: + +.. code-block:: php + + use ApacheSolrForTypo3\Solr\Event\Indexing\AfterPageDocumentIsCreatedForIndexingEvent; + + class MyEventListener { + + public function __invoke(AfterPageDocumentIsCreatedForIndexingEvent $event): void + { + $event->setDocument($myCustomDocument); + } + } -Registration with: $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['Indexer']['indexPageSubstitutePageDocument'] -Required Interface: SubstitutePageIndexer BeforeDocumentsAreIndexedEvent diff --git a/Documentation/Releases/solr-release-12-0.rst b/Documentation/Releases/solr-release-12-0.rst index 3f4b704103..182bd84c6a 100644 --- a/Documentation/Releases/solr-release-12-0.rst +++ b/Documentation/Releases/solr-release-12-0.rst @@ -18,6 +18,38 @@ Support of TYPO3 12 LTS With EXT:solr 12.0 we provide the support of TYPO3 12 LTS. +Hooks replaced by PSR-14 events +------------------------------- + +The previously available hooks and their respective interfaces have been removed from EXT:solr. + +The hook :php:`$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['Indexer']['indexPageAddDocuments']` and its +interface :php:`ApacheSolrForTypo3\Solr\AdditionalPageIndexer` are now superseded +by the PSR-14 event :php:`ApacheSolrForTypo3\Solr\Event\Indexing\BeforePageDocumentIsProcessedForIndexingEvent`. + +The hook :php:`$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['preAddModifyDocuments']` and its +interface :php:`ApacheSolrForTypo3\Solr\PageIndexerDocumentsModifier` are now superseded +by the PSR-14 event :php:`ApacheSolrForTypo3\Solr\Event\Indexing\BeforeDocumentIsProcessedForIndexingEvent`. + +The hook :php:`$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['indexItemAddDocuments']` and its +interface :php:`ApacheSolrForTypo3\Solr\AdditionalIndexQueueItemIndexer` are now superseded +by the PSR-14 event :php:`ApacheSolrForTypo3\Solr\Event\Indexing\BeforeDocumentsAreIndexedEvent`. + +The hook :php:`$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['Indexer']['indexPageSubstitutePageDocument']` and its +interface :php:`ApacheSolrForTypo3\Solr\SubstitutePageIndexer` are now superseded +by the PSR-14 event :php:`ApacheSolrForTypo3\Solr\Event\Indexing\AfterPageDocumentIsCreatedForIndexingEvent`. + +The hook :php:`$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessIndexQueueInitialization']` and its +interface :php:`ApacheSolrForTypo3\Solr\IndexQueue\InitializationPostProcessor` are now superseded +by the PSR-14 event :php:`ApacheSolrForTypo3\Solr\Event\IndexQueue\AfterIndexQueueHasBeenInitializedEvent` + +The hook :php:`$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessFetchRecordsForIndexQueueItem']` is now superseded +by the PSR-14 event :php:`ApacheSolrForTypo3\Solr\Event\IndexQueue\AfterRecordsForIndexQueueItemsHaveBeenRetrievedEvent` + +The hook :php:`$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueuePageIndexer']['dataUrlModifier']` +and the according interface :php:`ApacheSolrForTypo3\Solr\IndexQueue\PageIndexerDataUrlModifier` +is now superseded by the PSR-14 event :php:`ApacheSolrForTypo3\Solr\Event\Indexing\AfterFrontendPageUriForIndexingHasBeenGeneratedEvent` + Contributors ============ diff --git a/Resources/Private/Language/de.locallang_csh_pages.xlf b/Resources/Private/Language/de.locallang_csh_pages.xlf deleted file mode 100644 index 504757410a..0000000000 --- a/Resources/Private/Language/de.locallang_csh_pages.xlf +++ /dev/null @@ -1,12 +0,0 @@ - - - -
- - - If enabled, this option includes the sub entries of this page in Solr index. (NOTE: Value=0 means enabled in this view.) - Falls aktiviert, werden die Untereinträge dieser Seite bei einer Suche berücksichtigt. (Bitte beachte, dass der Wert=0 wegen Negation in dieser Ansicht "aktiviert" bedeutet.) - - - - diff --git a/Resources/Private/Language/locallang_csh_pages.xlf b/Resources/Private/Language/locallang_csh_pages.xlf deleted file mode 100644 index f04dc06b63..0000000000 --- a/Resources/Private/Language/locallang_csh_pages.xlf +++ /dev/null @@ -1,11 +0,0 @@ - - - -
- - - If enabled, this option includes the sub entries of this page in Solr index. (NOTE: Value=0 means enabled in this view.) - - - - diff --git a/Resources/Private/Templates/Backend/Search/InfoModule/Index.html b/Resources/Private/Templates/Backend/Search/InfoModule/Index.html index 9fd95da061..50de0f57a2 100644 --- a/Resources/Private/Templates/Backend/Search/InfoModule/Index.html +++ b/Resources/Private/Templates/Backend/Search/InfoModule/Index.html @@ -315,19 +315,11 @@

- - - - - + - - + diff --git a/Tests/Integration/Controller/SearchControllerTest.php b/Tests/Integration/Controller/SearchControllerTest.php index 8561124cf1..faefd81d2b 100644 --- a/Tests/Integration/Controller/SearchControllerTest.php +++ b/Tests/Integration/Controller/SearchControllerTest.php @@ -18,7 +18,6 @@ namespace ApacheSolrForTypo3\Solr\Tests\Integration\Controller; use ApacheSolrForTypo3\Solr\Controller\SearchController; -use ApacheSolrForTypo3\Solr\IndexQueue\FrontendHelper\PageFieldMappingIndexer; use ApacheSolrForTypo3\Solr\Tests\Integration\IntegrationTest; use DOMDocument; use TYPO3\CMS\Core\Http\Response; @@ -45,18 +44,6 @@ class SearchControllerTest extends IntegrationTest */ protected $searchResponse; - protected array $configurationToUseInTestInstance = [ - 'EXTCONF' => [ - 'solr' => [ - 'Indexer' => [ - 'indexPageSubstitutePageDocument' => [ - PageFieldMappingIndexer::class => PageFieldMappingIndexer::class, - ], - ], - ], - ], - ]; - protected function setUp(): void { parent::setUp(); diff --git a/Tests/Integration/Controller/SuggestControllerTest.php b/Tests/Integration/Controller/SuggestControllerTest.php index 06700d54c6..1acd45ddad 100644 --- a/Tests/Integration/Controller/SuggestControllerTest.php +++ b/Tests/Integration/Controller/SuggestControllerTest.php @@ -16,7 +16,6 @@ namespace ApacheSolrForTypo3\Solr\Tests\Integration\Controller; use ApacheSolrForTypo3\Solr\Controller\SuggestController; -use ApacheSolrForTypo3\Solr\IndexQueue\FrontendHelper\PageFieldMappingIndexer; use ApacheSolrForTypo3\Solr\Tests\Integration\IntegrationTest; use TYPO3\CMS\Core\Http\Response; use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest; @@ -29,18 +28,6 @@ */ class SuggestControllerTest extends IntegrationTest { - protected array $configurationToUseInTestInstance = [ - 'EXTCONF' => [ - 'solr' => [ - 'Indexer' => [ - 'indexPageSubstitutePageDocument' => [ - PageFieldMappingIndexer::class => PageFieldMappingIndexer::class, - ], - ], - ], - ], - ]; - protected function setUp(): void { parent::setUp(); diff --git a/Tests/Integration/Domain/Index/PageIndexer/PageUriBuilderTest.php b/Tests/Integration/Domain/Index/PageIndexer/PageUriBuilderTest.php new file mode 100644 index 0000000000..788550437c --- /dev/null +++ b/Tests/Integration/Domain/Index/PageIndexer/PageUriBuilderTest.php @@ -0,0 +1,54 @@ +writeDefaultSolrTestSiteConfiguration(); + } + + /** + * @test + */ + public function pageIndexingUriCanBeModifiedViaEventListener(): void + { + $subject = GeneralUtility::makeInstance(PageUriBuilder::class); + $item = new Item(['item_uid' => 1, 'root' => 1, 'item_type' => 'pages']); + $url = $subject->getPageIndexingUriFromPageItemAndLanguageId($item); + self::assertEquals('http://testone.site/en/', (string)$url); + $item->setIndexingProperty('size', 'enorme'); + $url = $subject->getPageIndexingUriFromPageItemAndLanguageId($item); + self::assertEquals('http://testone.site/en/?&larger=large', (string)$url); + } +} diff --git a/Tests/Integration/Domain/Index/Queue/QueueItemRepositoryTest.php b/Tests/Integration/Domain/Index/Queue/QueueItemRepositoryTest.php index 8dddb8d9c1..bbb15ca036 100644 --- a/Tests/Integration/Domain/Index/Queue/QueueItemRepositoryTest.php +++ b/Tests/Integration/Domain/Index/Queue/QueueItemRepositoryTest.php @@ -1,5 +1,7 @@ getType(), 'First item has unexpected type'); } + /** + * @test + */ + public function canFindItemsAndModifyViaEventListener(): void + { + $this->importCSVDataSet(__DIR__ . '/Fixtures/pages_and_news_queueitems.csv'); + $eventDispatcher = new MockEventDispatcher(); + $queueItemRepository = GeneralUtility::makeInstance(QueueItemRepository::class, null, $eventDispatcher); + $items = $queueItemRepository->findItems([], ['pages']); + + /** @var Item $firstItem */ + $firstItem = $items[0]; + self::assertSame(2, count($items)); + self::assertSame('pages', $firstItem->getType(), 'First item has unexpected type'); + + $eventDispatcher->addListener(function (AfterRecordsForIndexQueueItemsHaveBeenRetrievedEvent $event): void { + if ($event->getTable() === 'pages') { + $event->setRecords([]); + } + }); + + $items = $queueItemRepository->findItems([], ['pages']); + self::assertCount(0, $items); + } + /** * @test */ diff --git a/Tests/Integration/Fixtures/Extensions/fake_extension3/Classes/EventListeners/TestPageUriModification.php b/Tests/Integration/Fixtures/Extensions/fake_extension3/Classes/EventListeners/TestPageUriModification.php new file mode 100644 index 0000000000..d49056b466 --- /dev/null +++ b/Tests/Integration/Fixtures/Extensions/fake_extension3/Classes/EventListeners/TestPageUriModification.php @@ -0,0 +1,35 @@ +getItem()->hasIndexingProperty('size')) { + return; + } + if ($event->getItem()->getIndexingProperty('size') === 'enorme') { + $event->setPageIndexUri( + $event->getPageIndexUri()->withQuery('&larger=large') + ); + } + } +} diff --git a/Tests/Integration/Fixtures/Extensions/fake_extension3/Configuration/Services.yaml b/Tests/Integration/Fixtures/Extensions/fake_extension3/Configuration/Services.yaml index f848a6c538..2aab67721b 100644 --- a/Tests/Integration/Fixtures/Extensions/fake_extension3/Configuration/Services.yaml +++ b/Tests/Integration/Fixtures/Extensions/fake_extension3/Configuration/Services.yaml @@ -13,3 +13,8 @@ services: tags: - name: event.listener identifier: 'fake-extension3/test-modification-of-documents' + + ApacheSolrForTypo3\SolrFakeExtension3\EventListeners\TestPageUriModification: + tags: + - name: event.listener + identifier: 'fake-extension3/test-page-uri-modification' diff --git a/Tests/Integration/IndexQueue/FrontendHelper/PageIndexerTest.php b/Tests/Integration/IndexQueue/FrontendHelper/PageIndexerTest.php index 59c670d180..737a0a3261 100644 --- a/Tests/Integration/IndexQueue/FrontendHelper/PageIndexerTest.php +++ b/Tests/Integration/IndexQueue/FrontendHelper/PageIndexerTest.php @@ -17,7 +17,6 @@ namespace ApacheSolrForTypo3\Solr\Tests\Integration\IndexQueue\FrontendHelper; -use ApacheSolrForTypo3\Solr\AdditionalFieldsIndexer; use ApacheSolrForTypo3\Solr\IndexQueue\Item; use ApacheSolrForTypo3\Solr\Tests\Integration\IntegrationTest; use Psr\Http\Message\ResponseInterface; @@ -158,10 +157,6 @@ public function canIndexPageToCategoryRelation() */ public function canIndexPageIntoSolrWithAdditionalFields() { - //@todo additional fields indexer requires the hook to be activated which is normally done in ext_localconf.php - // this needs to be unified with the PageFieldMappingIndexer registration. - $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['Indexer']['indexPageSubstitutePageDocument']['ApacheSolrForTypo3\Solr\AdditionalFieldsIndexer'] = AdditionalFieldsIndexer::class; - $this->importCSVDataSet(__DIR__ . '/Fixtures/can_index_with_additional_fields_into_solr.csv'); $this->indexQueuedPage(); diff --git a/Tests/Integration/SearchTest.php b/Tests/Integration/SearchTest.php index 077b4dc5bf..1c38be070a 100644 --- a/Tests/Integration/SearchTest.php +++ b/Tests/Integration/SearchTest.php @@ -22,11 +22,11 @@ use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\TrigramPhraseFields; use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; +use ApacheSolrForTypo3\Solr\IndexQueue\FrontendHelper\PageIndexer; use ApacheSolrForTypo3\Solr\IndexQueue\Item; use ApacheSolrForTypo3\Solr\Search; use ApacheSolrForTypo3\Solr\System\Configuration\ConfigurationManager; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; -use ApacheSolrForTypo3\Solr\Typo3PageIndexer; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; @@ -64,15 +64,12 @@ public function canSearchForADocument(): void { $this->importCSVDataSet(__DIR__ . '/Fixtures/Search/can_search.csv'); - $fakeTSFE = $this->getConfiguredTSFE(); - - $pageIndexer = GeneralUtility::makeInstance(Typo3PageIndexer::class, $fakeTSFE); - $indexQueueItemMock = $this->createMock(Item::class); - $indexQueueItemMock->expects(self::any()) - ->method('getIndexingConfigurationName') - ->willReturn('pages'); - $pageIndexer->setIndexQueueItem($indexQueueItemMock); - $pageIndexer->indexPage(); + $tsfe = $this->getConfiguredTSFE(); + $pageIndexer = GeneralUtility::makeInstance(PageIndexer::class); + $indexQueueItem = new Item(['root' => 1, 'indexing_configuration' => 'pages', 'item_type' => 'pages']); + $indexQueueItem->setRecord(['uid' => 1]); + $pageIndexer->setupConfiguration(); + $pageIndexer->index($indexQueueItem, $tsfe); $this->waitToBeVisibleInSolr(); @@ -425,15 +422,12 @@ public function explicitPhraseSearchPrecisionCanBeAdjustedByQuerySlop(): void protected function fillIndexForPhraseSearchTests(): void { for ($i = 1; $i <= 15; $i++) { - $fakeTSFE = $this->getConfiguredTSFE($i); - - $pageIndexer = GeneralUtility::makeInstance(Typo3PageIndexer::class, $fakeTSFE); - $indexQueueItemMock = $this->createMock(Item::class); - $indexQueueItemMock->expects(self::any()) - ->method('getIndexingConfigurationName') - ->willReturn('pages'); - $pageIndexer->setIndexQueueItem($indexQueueItemMock); - $pageIndexer->indexPage(); + $tsfe = $this->getConfiguredTSFE($i); + $pageIndexer = GeneralUtility::makeInstance(PageIndexer::class); + $indexQueueItem = new Item(['root' => 1, 'indexing_configuration' => 'pages', 'item_type' => 'pages']); + $indexQueueItem->setRecord(['uid' => $i]); + $pageIndexer->setupConfiguration(); + $pageIndexer->index($indexQueueItem, $tsfe); } $this->waitToBeVisibleInSolr(); } diff --git a/Tests/Integration/TSFETestBootstrapper.php b/Tests/Integration/TSFETestBootstrapper.php index 5646a90109..99e71e9e3a 100644 --- a/Tests/Integration/TSFETestBootstrapper.php +++ b/Tests/Integration/TSFETestBootstrapper.php @@ -37,6 +37,7 @@ public function bootstrap(int $pageId): TypoScriptFrontendController $TSFE->determineId($request); $GLOBALS['TYPO3_REQUEST'] = $TSFE->getFromCache($request); $TSFE->releaseLocks(); + $TSFE->newCObj($GLOBALS['TYPO3_REQUEST']); return $TSFE; } } diff --git a/Tests/Unit/ConnectionManagerTest.php b/Tests/Unit/ConnectionManagerTest.php index 928e6b2670..9962868a0a 100644 --- a/Tests/Unit/ConnectionManagerTest.php +++ b/Tests/Unit/ConnectionManagerTest.php @@ -21,7 +21,6 @@ use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager; use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository; -use ApacheSolrForTypo3\Solr\System\Solr\Node; use ApacheSolrForTypo3\Solr\System\Solr\Parser\SchemaParser; use ApacheSolrForTypo3\Solr\System\Solr\Parser\StopWordParser; use ApacheSolrForTypo3\Solr\System\Solr\Parser\SynonymParser; @@ -31,6 +30,7 @@ use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; +use Solarium\Core\Client\Endpoint; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; use UnexpectedValueException; @@ -92,8 +92,8 @@ public function canConnect(string $host, string|int $port, string $path, string $self = $this; $this->connectionManager->expects(self::once())->method('getSolrConnectionForNodes')->willReturnCallback( function ($readNode, $writeNode) use ($self) { - $readNode = Node::fromArray($readNode); - $writeNode = Node::fromArray($writeNode); + $readNode = new Endpoint($readNode); + $writeNode = new Endpoint($writeNode); $typoScriptConfigurationMock = $self->createMock(TypoScriptConfiguration::class); $synonymsParserMock = $self->createMock(SynonymParser::class); $stopWordParserMock = $self->createMock(StopWordParser::class); diff --git a/Tests/Unit/Domain/Index/PageIndexer/Helper/UriBuilder/TYPO3SiteStrategyTest.php b/Tests/Unit/Domain/Index/PageIndexer/PageUriBuilderTest.php similarity index 69% rename from Tests/Unit/Domain/Index/PageIndexer/Helper/UriBuilder/TYPO3SiteStrategyTest.php rename to Tests/Unit/Domain/Index/PageIndexer/PageUriBuilderTest.php index 5e61dc23d5..bb4a01f836 100644 --- a/Tests/Unit/Domain/Index/PageIndexer/Helper/UriBuilder/TYPO3SiteStrategyTest.php +++ b/Tests/Unit/Domain/Index/PageIndexer/PageUriBuilderTest.php @@ -1,18 +1,21 @@ createMock(SolrLogManager::class); - $typo3SiteStrategy = GeneralUtility::makeInstance(TYPO3SiteStrategy::class, $loggerMock, $siteFinderMock); - $typo3SiteStrategy->getPageIndexingUriFromPageItemAndLanguageId($itemMock, 2, 'foo'); + $uriBuilder = GeneralUtility::makeInstance(PageUriBuilder::class, $loggerMock, $siteFinderMock, new NoopEventDispatcher()); + $uriBuilder->getPageIndexingUriFromPageItemAndLanguageId($itemMock, 2, 'foo'); } /** @@ -44,8 +47,8 @@ public function canOverrideHost(): void $loggerMock = $this->createMock(SolrLogManager::class); - $typo3SiteStrategy = GeneralUtility::makeInstance(TYPO3SiteStrategy::class, $loggerMock, $siteFinderMock); - $uri = $typo3SiteStrategy->getPageIndexingUriFromPageItemAndLanguageId($itemMock, 2, 'foo', ['frontendDataHelper.' => ['host' => 'www.secondsite.de']]); + $uriBuilder = GeneralUtility::makeInstance(PageUriBuilder::class, $loggerMock, $siteFinderMock, new NoopEventDispatcher()); + $uri = $uriBuilder->getPageIndexingUriFromPageItemAndLanguageId($itemMock, 2, 'foo', ['frontendDataHelper.' => ['host' => 'www.secondsite.de']]); self::assertSame('http://www.secondsite.de/en/test', $uri, 'Solr site strategy generated unexpected uri'); } @@ -62,19 +65,18 @@ public function canOverrideScheme(): void $loggerMock = $this->createMock(SolrLogManager::class); - $typo3SiteStrategy = GeneralUtility::makeInstance(TYPO3SiteStrategy::class, $loggerMock, $siteFinderMock); - $uri = $typo3SiteStrategy->getPageIndexingUriFromPageItemAndLanguageId($itemMock, 2, 'foo', ['frontendDataHelper.' => ['scheme' => 'https']]); + $uriBuilder = GeneralUtility::makeInstance(PageUriBuilder::class, $loggerMock, $siteFinderMock, new NoopEventDispatcher()); + $uri = $uriBuilder->getPageIndexingUriFromPageItemAndLanguageId($itemMock, 2, 'foo', ['frontendDataHelper.' => ['scheme' => 'https']]); self::assertSame('https://www.site.de/en/test', $uri, 'Solr site strategy generated unexpected uri'); } protected function getSiteFinderMock(array $pageRecord = []): SiteFinder { - $uriMock = $this->createMock(UriInterface::class); - $uriMock->expects(self::any())->method('__toString')->willReturn('http://www.site.de/en/test'); + $uri = new Uri('http://www.site.de/en/test'); $routerMock = $this->createMock(RouterInterface::class); $routerMock->expects(self::once())->method('generateUri')->with($pageRecord, ['_language' => 2, 'MP' => 'foo']) - ->willReturn($uriMock); + ->willReturn($uri); $siteMock = $this->createMock(Site::class); $siteMock->expects(self::once())->method('getRouter')->willReturn($routerMock); diff --git a/Tests/Unit/Domain/Index/Queue/QueueInitializerServiceTest.php b/Tests/Unit/Domain/Index/Queue/QueueInitializerServiceTest.php index b71cb2351b..9f8248f536 100644 --- a/Tests/Unit/Domain/Index/Queue/QueueInitializerServiceTest.php +++ b/Tests/Unit/Domain/Index/Queue/QueueInitializerServiceTest.php @@ -1,5 +1,22 @@ @@ -29,10 +47,10 @@ class QueueInitializerServiceTest extends SetUpUnitTestCase /** * @test */ - public function allIndexConfigurationsAreUsedWhenWildcardIsPassed() + public function allIndexConfigurationsAreUsedWhenWildcardIsPassed(): void { $queueMock = $this->createMock(Queue::class); - $service = $this->getMockBuilder(QueueInitializationService::class)->onlyMethods(['executeInitializer'])->setConstructorArgs([$queueMock])->getMock(); + $service = $this->getMockBuilder(QueueInitializationService::class)->onlyMethods(['executeInitializer'])->setConstructorArgs([$queueMock, new NoopEventDispatcher()])->getMock(); $fakeTs = [ 'plugin.' => [ diff --git a/Tests/Unit/IndexQueue/ItemTest.php b/Tests/Unit/IndexQueue/ItemTest.php index e93fc5d269..d64b9800a5 100644 --- a/Tests/Unit/IndexQueue/ItemTest.php +++ b/Tests/Unit/IndexQueue/ItemTest.php @@ -15,6 +15,7 @@ namespace ApacheSolrForTypo3\Solr\Tests\Unit\IndexQueue; +use ApacheSolrForTypo3\Solr\Domain\Index\Queue\QueueItemRepository; use ApacheSolrForTypo3\Solr\IndexQueue\Item; use ApacheSolrForTypo3\Solr\Tests\Unit\SetUpUnitTestCase; @@ -30,7 +31,7 @@ public function canGetErrors() { $metaData = ['errors' => 'error during index']; $record = []; - $item = new Item($metaData, $record); + $item = new Item($metaData, $record, null, $this->createMock(QueueItemRepository::class)); $errors = $item->getErrors(); self::assertSame('error during index', $errors, 'Can not get errors from queue item'); @@ -43,7 +44,7 @@ public function canGetType() { $metaData = ['item_type' => 'pages']; $record = []; - $item = new Item($metaData, $record); + $item = new Item($metaData, $record, null, $this->createMock(QueueItemRepository::class)); $type = $item->getType(); self::assertSame('pages', $type, 'Can not get type from queue item'); @@ -67,7 +68,7 @@ public function getStateDataProvider(): array */ public function canGetState($metaData, $expectedState) { - $item = new Item($metaData, []); + $item = new Item($metaData, [], null, $this->createMock(QueueItemRepository::class)); self::assertSame($expectedState, $item->getState(), 'Can not get state from item as expected'); } @@ -76,10 +77,10 @@ public function canGetState($metaData, $expectedState) */ public function testHasErrors() { - $item = new Item([], []); + $item = new Item([], [], null, $this->createMock(QueueItemRepository::class)); self::assertFalse($item->getHasErrors(), 'Expected that item without any data has no errors'); - $item = new Item(['errors' => 'something is broken'], []); + $item = new Item(['errors' => 'something is broken'], [], null, $this->createMock(QueueItemRepository::class)); self::assertTrue($item->getHasErrors(), 'Item with errors was not indicated to have errors'); } @@ -88,10 +89,10 @@ public function testHasErrors() */ public function testHasIndexingProperties() { - $item = new Item([], []); + $item = new Item([], [], null, $this->createMock(QueueItemRepository::class)); self::assertFalse($item->hasIndexingProperties(), 'Expected that empty item should not have any indexing properties'); - $item = new Item(['has_indexing_properties' => true], []); + $item = new Item(['has_indexing_properties' => true], [], null, $this->createMock(QueueItemRepository::class)); self::assertTrue($item->hasIndexingProperties(), 'Item with proper meta data should have indexing properties'); } } diff --git a/Tests/Unit/IndexQueue/PageIndexerTest.php b/Tests/Unit/IndexQueue/PageIndexerTest.php index 61b0690922..2fc38d69cb 100644 --- a/Tests/Unit/IndexQueue/PageIndexerTest.php +++ b/Tests/Unit/IndexQueue/PageIndexerTest.php @@ -17,7 +17,7 @@ use ApacheSolrForTypo3\Solr\Access\Rootline; use ApacheSolrForTypo3\Solr\ConnectionManager; -use ApacheSolrForTypo3\Solr\Domain\Index\PageIndexer\Helper\UriBuilder\AbstractUriStrategy; +use ApacheSolrForTypo3\Solr\Domain\Index\PageIndexer\PageUriBuilder; use ApacheSolrForTypo3\Solr\Domain\Search\ApacheSolrDocument\Builder; use ApacheSolrForTypo3\Solr\Domain\Site\Site; use ApacheSolrForTypo3\Solr\FrontendEnvironment; @@ -39,7 +39,7 @@ class PageIndexerTest extends SetUpUnitTestCase protected SolrLogManager|MockObject $solrLogManagerMock; protected ConnectionManager|MockObject $connectionManagerMock; protected PageIndexerRequest|MockObject $pageIndexerRequestMock; - protected AbstractUriStrategy|MockObject $uriStrategyMock; + protected PageUriBuilder|MockObject $uriBuilderMock; protected MockObject|FrontendEnvironment $frontendEnvironmentMock; protected function setUp(): void @@ -49,7 +49,7 @@ protected function setUp(): void $this->solrLogManagerMock = $this->createMock(SolrLogManager::class); $this->connectionManagerMock = $this->createMock(ConnectionManager::class); $this->pageIndexerRequestMock = $this->createMock(PageIndexerRequest::class); - $this->uriStrategyMock = $this->createMock(AbstractUriStrategy::class); + $this->uriBuilderMock = $this->createMock(PageUriBuilder::class); $this->frontendEnvironmentMock = $this->createMock(FrontendEnvironment::class); parent::setUp(); } @@ -68,10 +68,10 @@ protected function getPageIndexerWithMockedDependencies(array $options = []): Pa $this->createMock(EventDispatcherInterface::class), ] ) - ->onlyMethods(['getPageIndexerRequest', 'getAccessRootlineByPageId', 'getUriStrategy']) + ->onlyMethods(['getPageIndexerRequest', 'getAccessRootlineByPageId', 'getUriBuilder']) ->getMock(); $pageIndexer->expects(self::any())->method('getPageIndexerRequest')->willReturn($this->pageIndexerRequestMock); - $pageIndexer->expects(self::any())->method('getUriStrategy')->willReturn($this->uriStrategyMock); + $pageIndexer->expects(self::any())->method('getUriBuilder')->willReturn($this->uriBuilderMock); return $pageIndexer; } @@ -90,7 +90,7 @@ public function testIndexPageItemIsSendingFrontendRequestsToExpectedUrls(): void $siteMock->expects(self::any())->method('getRootPageRecord')->willReturn(['l18n_cfg' => 0, 'title' => 'mysiteroot']); $testUri = 'http://myfrontendurl.de/index.php?id=4711&L=0'; - $this->uriStrategyMock->expects(self::any())->method('getPageIndexingUriFromPageItemAndLanguageId')->willReturn($testUri); + $this->uriBuilderMock->expects(self::any())->method('getPageIndexingUriFromPageItemAndLanguageId')->willReturn($testUri); /** @var Item|MockObject $item */ $item = $this->createMock(Item::class); diff --git a/Tests/Unit/System/Solr/SolrConnectionTest.php b/Tests/Unit/System/Solr/SolrConnectionTest.php index 032ecbf3dc..4fdc741eda 100644 --- a/Tests/Unit/System/Solr/SolrConnectionTest.php +++ b/Tests/Unit/System/Solr/SolrConnectionTest.php @@ -17,7 +17,6 @@ use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager; -use ApacheSolrForTypo3\Solr\System\Solr\Node; use ApacheSolrForTypo3\Solr\System\Solr\Parser\SchemaParser; use ApacheSolrForTypo3\Solr\System\Solr\Parser\StopWordParser; use ApacheSolrForTypo3\Solr\System\Solr\Parser\SynonymParser; @@ -38,8 +37,8 @@ class SolrConnectionTest extends SetUpUnitTestCase { /** - * @param Node|null $readNode - * @param Node|null $writeNode + * @param Endpoint|null $readNode + * @param Endpoint|null $writeNode * @param TypoScriptConfiguration|null $configuration * @param SynonymParser|null $synonymParser * @param StopWordParser|null $stopWordParser @@ -49,11 +48,11 @@ class SolrConnectionTest extends SetUpUnitTestCase * @param RequestFactoryInterface|null $requestFactory * @param StreamFactoryInterface|null $streamFactory * @param EventDispatcherInterface|null $eventDispatcher - * @return SolrConnection + * @return SolrConnection|null */ protected function getSolrConnectionWithDummyConstructorArgs( - Node $readNode = null, - Node $writeNode = null, + Endpoint $readNode = null, + Endpoint $writeNode = null, TypoScriptConfiguration $configuration = null, SynonymParser $synonymParser = null, StopWordParser $stopWordParser = null, @@ -66,8 +65,8 @@ protected function getSolrConnectionWithDummyConstructorArgs( ): ?SolrConnection { try { return new SolrConnection( - $readNode ?? $this->createMock(Node::class), - $writeNode ?? $this->createMock(Node::class), + $readNode ?? $this->createMock(Endpoint::class), + $writeNode ?? $this->createMock(Endpoint::class), $configuration ?? $this->createMock(TypoScriptConfiguration::class), $synonymParser ?? $this->createMock(SynonymParser::class), $stopWordParser ?? $this->createMock(StopWordParser::class), @@ -93,7 +92,7 @@ public function authenticationIsNotTriggeredWithoutUsername() $clientMock = $this->createMock(Client::class); $clientMock->expects(self::any())->method('getEndpoints')->willReturn([$endpointMock]); - $readNode = Node::fromArray( + $readNode = new Endpoint( ['host' => 'localhost', 'port' => 8080, 'path' => '/solr/core_en/', 'scheme' => 'https', 'username' => '', 'password' => ''] ); $writeNode = $readNode; @@ -113,7 +112,7 @@ public function authenticationIsTriggeredWhenUsernameIsPassed() $clientMock = $this->createMock(Client::class); $clientMock->expects(self::any())->method('getEndpoints')->willReturn([$endpointMock]); - $readNode = Node::fromArray( + $readNode = new Endpoint( ['host' => 'localhost', 'port' => 8080, 'path' => '/solr/core_en/', 'scheme' => 'https', 'username' => 'foo', 'password' => 'bar'] ); $writeNode = $readNode; @@ -142,7 +141,7 @@ public function coreNameDataProvider(): array public function canGetCoreName($path, $expectedCoreName) { $fakeConfiguration = $this->createMock(TypoScriptConfiguration::class); - $readNode = Node::fromArray( + $readNode = new Endpoint( ['host' => 'localhost', 'port' => 8080, 'path' => $path, 'scheme' => 'http', 'username' => '', 'password' => ''] ); $writeNode = $readNode; @@ -167,7 +166,7 @@ public function coreBasePathDataProvider(): array */ public function canGetCoreBasePath($path, $expectedCoreBasePath) { - $readNode = Node::fromArray( + $readNode = new Endpoint( ['host' => 'localhost', 'port' => 8080, 'path' => $path, 'scheme' => 'http', 'username' => '', 'password' => ''] ); $writeNode = $readNode; @@ -180,7 +179,7 @@ public function canGetCoreBasePath($path, $expectedCoreBasePath) */ public function toStringContainsAllSegments() { - $readNode = Node::fromArray( + $readNode = new Endpoint( ['host' => 'localhost', 'port' => 8080, 'path' => '/core_de/', 'scheme' => 'http', 'username' => '', 'password' => ''] ); $writeNode = $readNode; diff --git a/ext_localconf.php b/ext_localconf.php index 30292da154..b54427c4e8 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -21,8 +21,6 @@ // ----- # ----- # ----- # ----- # ----- # ----- # ----- # ----- # ----- # // registering Index Queue page indexer helpers - $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['Indexer']['indexPageSubstitutePageDocument'][\ApacheSolrForTypo3\Solr\AdditionalFieldsIndexer::class] = \ApacheSolrForTypo3\Solr\AdditionalFieldsIndexer::class; - \ApacheSolrForTypo3\Solr\IndexQueue\FrontendHelper\Manager::registerFrontendHelper( 'findUserGroups', \ApacheSolrForTypo3\Solr\IndexQueue\FrontendHelper\UserGroupDetector::class diff --git a/ext_tables.php b/ext_tables.php index 0d8ab18d17..7a495707eb 100644 --- a/ext_tables.php +++ b/ext_tables.php @@ -1,25 +1,9 @@ isBackend() - ) { - // Register Context Sensitive Help (CSH) translation labels - ExtensionManagementUtility::addLLrefForTCAdescr( - 'pages', - 'EXT:solr/Resources/Private/Language/locallang_csh_pages.xlf' - ); - } -})(); - -// ----- # ----- # ----- # ----- # ----- # ----- # ----- # ----- # ----- # - $isComposerMode = defined('TYPO3_COMPOSER_MODE') && TYPO3_COMPOSER_MODE; if (!$isComposerMode) { // we load the autoloader for our libraries