diff --git a/Classes/Controller/BackendModuleController.php b/Classes/Controller/BackendModuleController.php index e4792d5f..c49d8a93 100644 --- a/Classes/Controller/BackendModuleController.php +++ b/Classes/Controller/BackendModuleController.php @@ -179,25 +179,31 @@ public function startIndexingAction(ServerRequestInterface $request, ModuleTempl // show information about indexer configurations and number of records // if action "start indexing" is not selected if ($this->do != 'startindexer') { + $content .= '
'; $content .= $this->printNumberOfRecords(); $content .= $this->printIndexerConfigurations($indexerConfigurations); + $content .= '
'; } // show "start indexing" or "remove lock" button if ($lockTime !== 0) { if (!$this->getBackendUser()->isAdmin()) { // print warning message for non-admins + $content .= '
'; $content .= '
'; $content .= '

WARNING!

'; $content .= '

The indexer is already running and can not be started twice.

'; $content .= '
'; + $content .= '
'; } else { // show 'remove lock' button for admins + $content .= '
'; $content .= '
'; $content .= '

The indexer is already running and can not be started twice.

'; $content .= '
'; $content .= '

The indexing process was started at ' . SearchHelper::formatTimestamp($lockTime) . '.

'; $content .= '

You can remove the lock by clicking the following button.

'; + $content .= '
'; $moduleUrl = $uriBuilder->buildUriFromRoute( 'web_KeSearchBackendModule', [ @@ -205,7 +211,14 @@ public function startIndexingAction(ServerRequestInterface $request, ModuleTempl 'do' => 'rmLock', ] ); - $content .= '

Remove Lock

'; + $content .= 'Remove Lock '; + $moduleUrl = $uriBuilder->buildUriFromRoute( + 'web_KeSearchBackendModule', + [ + 'id' => $this->pageId + ] + ); + $content .= ' Reload'; } } else { // no lock set - show "start indexer" link if indexer configurations have been found @@ -217,9 +230,9 @@ public function startIndexingAction(ServerRequestInterface $request, ModuleTempl 'do' => 'startindexer', ] ); - $content .= '' + $content .= '' . LocalizationUtility::translate('backend.start_indexer_full', 'ke_search') - . ' '; + . ''; $moduleUrl = $uriBuilder->buildUriFromRoute( 'web_KeSearchBackendModule', [ @@ -228,7 +241,7 @@ public function startIndexingAction(ServerRequestInterface $request, ModuleTempl 'indexingMode' => IndexerBase::INDEXING_MODE_INCREMENTAL, ] ); - $content .= '' + $content .= ' ' . LocalizationUtility::translate('backend.start_indexer_incremental', 'ke_search') . ''; } else { @@ -446,7 +459,7 @@ public function getLastIndexingReport() */ public function printIndexerConfigurations($indexerConfigurations) { - $content = '

Indexers

'; + $content = '

Indexers

'; // show indexer names if ($indexerConfigurations) { $content .= '
'; @@ -468,7 +481,7 @@ public function printIndexerConfigurations($indexerConfigurations) . ''; } $content .= '
'; - $content .= '
'; + $content .= ''; } return $content; @@ -481,7 +494,7 @@ public function printIndexerConfigurations($indexerConfigurations) */ public function printNumberOfRecords() { - $content = '

Index statistics

'; + $content = '

Index statistics

'; $numberOfRecords = $this->indexRepository->getTotalNumberOfRecords(); if ($numberOfRecords) { @@ -526,7 +539,7 @@ public function printNumberOfRecords() } $content .= '
'; - $content .= ''; + $content .= ''; } return $content; diff --git a/Classes/Controller/IndexerStatusController.php b/Classes/Controller/IndexerStatusController.php new file mode 100644 index 00000000..88d52af2 --- /dev/null +++ b/Classes/Controller/IndexerStatusController.php @@ -0,0 +1,60 @@ +responseFactory = $responseFactory; + $this->indexerStatusService = $indexerStatusService; + } + + public function getStatusAction(ServerRequestInterface $request): ResponseInterface + { + $isRunning = $this->indexerStatusService->isRunning(); + $indexerStatus = []; + $html = 'Indexer is idle.'; + + if ($isRunning) { + $indexerStartTime = $this->indexerStatusService->getIndexerStartTime(); + $indexerStatus = $this->indexerStatusService->getIndexerStatus(); + $html = 'Indexer is running since ' . TimeUtility::getRunningTimeHumanReadable($indexerStartTime) . '.'; + if ($indexerStatus['indexers'] ?? false) { + $html .= '

Indexers:
'; + foreach ($indexerStatus['indexers'] as $singleIndexerStatus) { + $statusLine = htmlspecialchars($singleIndexerStatus['statusText'], ENT_QUOTES, 'UTF-8') . '
'; + if ($singleIndexerStatus['status'] == $this->indexerStatusService::INDEXER_STATUS_RUNNING) { + $statusLine = '' . $statusLine . ''; + } + $html .= $statusLine; + } + } + } + + $result = [ + 'running' => $isRunning, + 'indexers' => $indexerStatus['indexers'] ?? [], + 'html' => $html, + ]; + + $response = $this->responseFactory->createResponse() + ->withHeader('Content-Type', 'application/json; charset=utf-8'); + $response->getBody()->write( + json_encode($result, JSON_THROW_ON_ERROR), + ); + + return $response; + } + +} diff --git a/Classes/Indexer/IndexerRunner.php b/Classes/Indexer/IndexerRunner.php index 676dec74..5189943d 100644 --- a/Classes/Indexer/IndexerRunner.php +++ b/Classes/Indexer/IndexerRunner.php @@ -29,11 +29,11 @@ use Tpwd\KeSearch\Event\ModifyFieldValuesBeforeStoringEvent; use Tpwd\KeSearch\Lib\Db; use Tpwd\KeSearch\Lib\SearchHelper; +use Tpwd\KeSearch\Service\IndexerStatusService; use Tpwd\KeSearch\Utility\AdditionalWordCharactersUtility; use TYPO3\CMS\Core\Log\Logger; use TYPO3\CMS\Core\Log\LogManager; use TYPO3\CMS\Core\Mail\MailMessage; -use TYPO3\CMS\Core\Registry; use TYPO3\CMS\Core\Utility\DebugUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\StringUtility; @@ -69,11 +69,6 @@ class IndexerRunner */ public $currentRow = []; - /** - * @var Registry - */ - public $registry; - /** * @var Logger */ @@ -86,21 +81,23 @@ class IndexerRunner private EventDispatcherInterface $eventDispatcher; private IndexRepository $indexRepository; + private IndexerStatusService $indexerStatusService; /** * Constructor of this class */ public function __construct( EventDispatcherInterface $eventDispatcher, - IndexRepository $indexRepository + IndexRepository $indexRepository, + IndexerStatusService $indexerStatusService ) { $this->eventDispatcher = $eventDispatcher; $this->indexRepository = $indexRepository; + $this->indexerStatusService = $indexerStatusService; // get extension configuration array $this->extConf = SearchHelper::getExtConf(); $this->extConfPremium = SearchHelper::getExtConfPremium(); - $this->registry = GeneralUtility::makeInstance(Registry::class); // fetch the list of the default indexers which come with ke_search foreach ($GLOBALS['TCA']['tx_kesearch_indexerconfig']['columns']['type']['config']['items'] as $indexerType) { @@ -121,7 +118,7 @@ public function __construct( */ public function startIndexing($verbose = true, $extConf = [], $mode = '', $indexingMode = IndexerBase::INDEXING_MODE_FULL) { - $content = '
'; + $content = '
'; $content .= '
'; $message = 'Running indexing process in ' . ($indexingMode == IndexerBase::INDEXING_MODE_FULL ? 'full' : 'incremental') . ' mode'; @@ -141,16 +138,16 @@ public function startIndexing($verbose = true, $extConf = [], $mode = '', $index // write starting timestamp into registry // this is a helper to delete all records which are older than starting timestamp in registry // this also prevents starting the indexer twice - if ($this->registry->get('tx_kesearch', 'startTimeOfIndexer') === null) { - $this->registry->set('tx_kesearch', 'startTimeOfIndexer', time()); + if (!$this->indexerStatusService->isRunning()) { + $this->indexerStatusService->startIndexerTime(); } else { // check lock time - $lockTime = $this->registry->get('tx_kesearch', 'startTimeOfIndexer'); + $lockTime = $this->indexerStatusService->getIndexerStartTime(); $compareTime = time() - (60 * 60 * 12); if ($lockTime < $compareTime) { // lock is older than 12 hours - remove - $this->registry->remove('tx_kesearch', 'startTimeOfIndexer'); - $this->registry->set('tx_kesearch', 'startTimeOfIndexer', time()); + $this->indexerStatusService->clearIndexerStartTime(); + $this->indexerStatusService->startIndexerTime(); $this->logger->notice('lock has been removed because it is older than 12 hours' . time()); } else { $this->logger->warning('lock is set, you can\'t start indexer twice.'); @@ -179,6 +176,10 @@ public function startIndexing($verbose = true, $extConf = [], $mode = '', $index $content .= '
'; $content .= ''; foreach ($configurations as $indexerConfig) { + $this->indexerStatusService->setScheduledStatus($indexerConfig); + } + foreach ($configurations as $indexerConfig) { + $this->indexerStatusService->setRunningStatus($indexerConfig); $this->indexerConfig = $indexerConfig; // run default indexers shipped with ke_search @@ -229,6 +230,7 @@ public function startIndexing($verbose = true, $extConf = [], $mode = '', $index } } } + $this->indexerStatusService->setFinishedStatus($indexerConfig); } $content .= '
IndexerModeInfoTime
'; @@ -256,11 +258,7 @@ public function startIndexing($verbose = true, $extConf = [], $mode = '', $index $content .= '
'; $content .= '
'; - $this->registry->set( - 'tx_kesearch', - 'lastRun', - ['startTime' => $this->startTime, 'endTime' => $this->endTime, 'indexingTime' => $indexingTime] - ); + $this->indexerStatusService->setLastRunTime($this->startTime, $this->endTime, $indexingTime); // create plaintext report $plaintextReport = $this->createPlaintextReport($content); @@ -491,8 +489,7 @@ public function cleanUpProcessAfterIndexing() Db::getDatabaseConnection('tx_kesearch_index') ->exec('DEALLOCATE PREPARE insertStmt'); - // remove all entries from ke_search registry - $this->registry->removeAllByNamespace('tx_kesearch'); + $this->indexerStatusService->clearAll(); } /** @@ -512,10 +509,7 @@ public function cleanUpIndex(int $indexingMode) $queryBuilder = Db::getQueryBuilder('tx_kesearch_index'); $where = $queryBuilder->expr()->lt( 'tstamp', - $queryBuilder->quote( - $this->registry->get('tx_kesearch', 'startTimeOfIndexer'), - PDO::PARAM_INT - ) + $queryBuilder->quote($this->indexerStatusService->getIndexerStartTime(), PDO::PARAM_INT) ); // hook for cleanup diff --git a/Classes/Indexer/Types/News.php b/Classes/Indexer/Types/News.php index 4a2b135d..23ea6a82 100644 --- a/Classes/Indexer/Types/News.php +++ b/Classes/Indexer/Types/News.php @@ -27,6 +27,7 @@ use Tpwd\KeSearch\Indexer\IndexerRunner; use Tpwd\KeSearch\Lib\Db; use Tpwd\KeSearch\Lib\SearchHelper; +use Tpwd\KeSearch\Service\IndexerStatusService; use Tpwd\KeSearch\Utility\ContentUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -38,6 +39,11 @@ */ class News extends IndexerBase { + /** + * @var IndexerStatusService + */ + private $indexerStatusService; + /** * Initializes indexer for news * @@ -47,6 +53,7 @@ public function __construct($pObj) { parent::__construct($pObj); $this->pObj = $pObj; + $this->indexerStatusService = GeneralUtility::makeInstance(IndexerStatusService::class); } /** @@ -125,6 +132,7 @@ public function startIndexing() if ($resCount) { while (($newsRecord = $res->fetchAssociative())) { $shouldBeIndexed = true; + $this->indexerStatusService->setRunningStatus($this->indexerConfig, $indexedNewsCounter, $resCount); // get category data for this news record (list of // assigned categories and single view from category, if it exists) diff --git a/Classes/Indexer/Types/Page.php b/Classes/Indexer/Types/Page.php index 07509bdb..e13f3d58 100644 --- a/Classes/Indexer/Types/Page.php +++ b/Classes/Indexer/Types/Page.php @@ -36,6 +36,7 @@ use Tpwd\KeSearch\Lib\Db; use Tpwd\KeSearch\Lib\SearchHelper; use Tpwd\KeSearch\Service\AdditionalContentService; +use Tpwd\KeSearch\Service\IndexerStatusService; use Tpwd\KeSearch\Utility\ContentUtility; use Tpwd\KeSearch\Utility\FileUtility; use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider; @@ -155,6 +156,8 @@ class Page extends IndexerBase */ protected AdditionalContentService $additionalContentService; + private IndexerStatusService $indexerStatusService; + /** * tx_kesearch_indexer_types_page constructor. * @param IndexerRunner $pObj @@ -213,6 +216,8 @@ public function __construct($pObj) // make filesProcessor $this->filesProcessor = GeneralUtility::makeInstance(FilesProcessor::class); + + $this->indexerStatusService = GeneralUtility::makeInstance(IndexerStatusService::class); } /** @@ -261,7 +266,9 @@ public function startIndexing() $this->addTagsToRecords($indexPids, $where); // loop through pids and collect page content and tags + $counter = 0; foreach ($indexPids as $uid) { + $this->indexerStatusService->setRunningStatus($this->indexerConfig, $counter, count($indexPids)); $this->getPageContent($uid); } diff --git a/Classes/Service/IndexerStatusService.php b/Classes/Service/IndexerStatusService.php new file mode 100644 index 00000000..fe365bfd --- /dev/null +++ b/Classes/Service/IndexerStatusService.php @@ -0,0 +1,135 @@ +registry = $registry; + } + + public function isRunning(): bool + { + return (bool)SearchHelper::getIndexerStartTime(); + } + + public function getIndexerStartTime(): int + { + return SearchHelper::getIndexerStartTime(); + } + + public function startIndexerTime(): void + { + $this->registry->set( + self::INDEXER_STATUS_REGISTRY_NAMESPACE, + self::INDEXER_STATUS_REGISTRY_START_TIME_KEY, + time() + ); + } + + public function clearIndexerStartTime(): void + { + $this->registry->remove( + self::INDEXER_STATUS_REGISTRY_NAMESPACE, + self::INDEXER_STATUS_REGISTRY_START_TIME_KEY + ); + } + + public function setScheduledStatus(array $indexerConfig) + { + $indexerStatus = $this->getIndexerStatus(); + $indexerStatus['indexers'][$indexerConfig['uid']] = [ + 'status' => self::INDEXER_STATUS_SCHEDULED, + 'currentRecordCount' => 0, + 'totalRecords' => 0, + 'statusText' => + '"' . $indexerConfig['title'] . '"' + . ' is scheduled for execution' + ]; + $this->setIndexerStatus($indexerStatus); + } + + public function setFinishedStatus(array $indexerConfig) + { + $indexerStatus = $this->getIndexerStatus(); + $indexerStatus['indexers'][$indexerConfig['uid']]['status'] + = self::INDEXER_STATUS_FINISHED; + $indexerStatus['indexers'][$indexerConfig['uid']]['statusText'] + = '"' . $indexerConfig['title'] . '"' + . ' has finished'; + if ($indexerStatus['indexers'][$indexerConfig['uid']]['totalRecords'] >= 0) { + $indexerStatus['indexers'][$indexerConfig['uid']]['statusText'] .= + ' (' . $indexerStatus['indexers'][$indexerConfig['uid']]['totalRecords'] . ' records)'; + } + $this->setIndexerStatus($indexerStatus); + } + + public function setRunningStatus(array $indexerConfig, int $currentRecordCount = -1, int $totalRecordCount = -1) + { + $indexerStatus = $this->getIndexerStatus(); + $indexerStatus['indexers'][$indexerConfig['uid']] = [ + 'status' => self::INDEXER_STATUS_RUNNING, + 'currentRecordCount' => $currentRecordCount, + 'totalRecords' => $totalRecordCount, + 'statusText' => + '"' . $indexerConfig['title'] . '"' + . ' is running' + . ' (' . $currentRecordCount . ' / ' . $totalRecordCount . ' records)' + ]; + $this->setIndexerStatus($indexerStatus); + } + + public function getIndexerStatus(): array + { + return $this->registry->get( + self::INDEXER_STATUS_REGISTRY_NAMESPACE, + self::INDEXER_STATUS_REGISTRY_STATUS_KEY + ) ?? []; + } + + public function setIndexerStatus(array $indexerStatus): void + { + $this->registry->set( + self::INDEXER_STATUS_REGISTRY_NAMESPACE, + self::INDEXER_STATUS_REGISTRY_STATUS_KEY, + $indexerStatus + ); + } + + public function setLastRunTime(int $startTime, int $endTime, int $indexingTime): void + { + $this->registry->set( + self::INDEXER_STATUS_REGISTRY_NAMESPACE, + self::INDEXER_STATUS_REGISTRY_LAST_RUN_TIME, + [ + 'startTime' => $startTime, + 'endTime' => $endTime, + 'indexingTime' => $indexingTime + ] + ); + } + + /** + * removes all entries from ke_search registry + * + * @return void + */ + public function clearAll(): void + { + $this->registry->removeAllByNamespace(self::INDEXER_STATUS_REGISTRY_NAMESPACE); + } +} diff --git a/Classes/Utility/TimeUtility.php b/Classes/Utility/TimeUtility.php new file mode 100644 index 00000000..61ca0a9e --- /dev/null +++ b/Classes/Utility/TimeUtility.php @@ -0,0 +1,46 @@ + 0) ? + [ + 'h' => floor($time / 3600), + 'm' => (int)($time / 60) % 60, + 's' => $time % 60, + ] + : []; + } + + public static function getRunningTimeHumanReadable(int $indexerStartTime): string + { + $indexerRunningTime = TimeUtility::getRunningTime($indexerStartTime); + if ($indexerRunningTime < 1) { + return ''; + } + $indexerRunningTimeHMS = TimeUtility::getTimeHoursMinutesSeconds($indexerRunningTime); + + $result = ''; + if ($indexerRunningTimeHMS['h']) { + $result .= ' ' . $indexerRunningTimeHMS['h'] . ' h'; + } + if ($indexerRunningTimeHMS['m']) { + $result .= ' ' . $indexerRunningTimeHMS['m'] . ' m'; + } + if ($indexerRunningTimeHMS['s']) { + $result .= ' ' . $indexerRunningTimeHMS['s'] . ' s'; + } + + return $result; + } + + +} diff --git a/Classes/Widgets/StatusWidget.php b/Classes/Widgets/StatusWidget.php index 2a577a65..58103b5b 100644 --- a/Classes/Widgets/StatusWidget.php +++ b/Classes/Widgets/StatusWidget.php @@ -23,6 +23,7 @@ namespace Tpwd\KeSearch\Widgets; use Tpwd\KeSearch\Lib\SearchHelper; +use Tpwd\KeSearch\Utility\TimeUtility; use TYPO3\CMS\Core\Information\Typo3Version; use TYPO3\CMS\Core\Registry; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -58,15 +59,8 @@ public function renderWidgetContent(): string } $indexerStartTime = SearchHelper::getIndexerStartTime(); - $indexerRunningTime = $indexerStartTime ? (time() - $indexerStartTime) : 0; - $indexerRunningTimeHMS = - $indexerRunningTime ? - [ - 'h' => floor($indexerRunningTime / 3600), - 'm' => (int)($indexerRunningTime / 60) % 60, - 's' => $indexerRunningTime % 60, - ] - : []; + $indexerRunningTime = TimeUtility::getRunningTime($indexerStartTime); + $indexerRunningTimeHMS = TimeUtility::getTimeHoursMinutesSeconds($indexerRunningTime); $this->view->assignMultiple([ 'configuration' => $this->configuration, 'indexerStartTime' => $indexerStartTime, @@ -76,14 +70,7 @@ public function renderWidgetContent(): string $lastRun = $this->registry->get('tx_kesearch', 'lastRun'); if (!empty($lastRun)) { - $lastRunIndexingTimeHMS = - $lastRun['indexingTime'] ? - [ - 'h' => floor($lastRun['indexingTime'] / 3600), - 'm' => (int)($lastRun['indexingTime'] / 60) % 60, - 's' => $lastRun['indexingTime'] % 60, - ] - : []; + $lastRunIndexingTimeHMS = TimeUtility::getTimeHoursMinutesSeconds($lastRun['indexingTime'] ?? 0); $this->view->assignMultiple([ 'lastRunStartTime' => $lastRun['startTime'], 'lastRunEndTime' => $lastRun['endTime'], diff --git a/Configuration/Backend/AjaxRoutes.php b/Configuration/Backend/AjaxRoutes.php new file mode 100644 index 00000000..00388c47 --- /dev/null +++ b/Configuration/Backend/AjaxRoutes.php @@ -0,0 +1,8 @@ + [ + 'path' => '/ke-search/indexer-status/get-status', + 'target' => \Tpwd\KeSearch\Controller\IndexerStatusController::class . '::getStatusAction', + ], +]; diff --git a/Configuration/JavaScriptModules.php b/Configuration/JavaScriptModules.php new file mode 100644 index 00000000..1a2db9d0 --- /dev/null +++ b/Configuration/JavaScriptModules.php @@ -0,0 +1,8 @@ + ['core', 'backend'], + 'imports' => [ + '@tpwd/ke-search/' => 'EXT:ke_search/Resources/Public/JavaScript/', + ], +]; diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 074cae88..ddedc714 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -11,6 +11,10 @@ services: resource: '../Classes/Service/*' public: true + Tpwd\KeSearch\Controller\: + resource: '../Classes/Controller/*' + public: true + connection.tx_kesearch_index: class: 'TYPO3\CMS\Core\Database\Connection' factory: ['@TYPO3\CMS\Core\Database\ConnectionPool', 'getConnectionForTable'] @@ -51,6 +55,3 @@ services: public: true arguments: $eventDispatcher: '@Psr\EventDispatcher\EventDispatcherInterface' - - Tpwd\KeSearch\Controller\BackendModuleController: - public: true diff --git a/Resources/Private/Templates/BackendModule/StartIndexing.html b/Resources/Private/Templates/BackendModule/StartIndexing.html index 5f7062f4..fcfa5464 100644 --- a/Resources/Private/Templates/BackendModule/StartIndexing.html +++ b/Resources/Private/Templates/BackendModule/StartIndexing.html @@ -7,7 +7,17 @@ +
+
+
Waiting for indexer status.
+
+
{content} +
diff --git a/Resources/Public/JavaScript/getIndexerStatusRequest.js b/Resources/Public/JavaScript/getIndexerStatusRequest.js new file mode 100644 index 00000000..b1136b2e --- /dev/null +++ b/Resources/Public/JavaScript/getIndexerStatusRequest.js @@ -0,0 +1,18 @@ +import AjaxRequest from "@typo3/core/ajax/ajax-request.js"; + +setInterval(function () { + new AjaxRequest(TYPO3.settings.ajaxUrls.kesearch_indexerstatus_getstatus) + .get() + .then(async function (response) { + const indexerStatus = await response.resolve(); + document.getElementById("kesearch-indexer-status").innerHTML = indexerStatus.html; + if (indexerStatus.running === true) { + let hideElements= ["kesearch-indexer-overview", "kesearch-button-start-full", "kesearch-indexer-report", "kesearch-button-start-incremental"]; + hideElements.forEach((element) => { + if (document.getElementById(element)) { + document.getElementById(element).style.display = 'none'; + } + }); + } + }); +}, 1000);