* All rights reserved * * This script is part of the TYPO3 project. The TYPO3 project is * free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * The GNU General Public License can be found at * http://www.gnu.org/copyleft/gpl.html. * * This script is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ use WapplerSystems\Meilisearch\Utility\ManagedResourcesUtility; use TYPO3\CMS\Backend\Template\ModuleTemplate; use TYPO3\CMS\Core\Messaging\FlashMessage; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\View\ViewInterface; /** * Manage Synonyms and Stop words in Backend Module * @property \TYPO3\CMS\Extbase\Mvc\Web\Response $response */ class CoreOptimizationModuleController extends AbstractModuleController { /** * Set up the doc header properly here * * @param ViewInterface $view * @return void */ protected function initializeView(ViewInterface $view) { parent::initializeView($view); $this->generateCoreSelectorMenuUsingPageTree(); /* @var ModuleTemplate $module */ // holds the state of chosen tab $module = $this->objectManager->get(ModuleTemplate::class); $coreOptimizationTabs = $module->getDynamicTabMenu([], 'coreOptimization'); $this->view->assign('tabs', $coreOptimizationTabs); } /** * Gets synonyms and stopwords for the currently selected core * * @return void */ public function indexAction() { if ($this->selectedSolrCoreConnection === null) { $this->view->assign('can_not_proceed', true); return; } $synonyms = []; $coreAdmin = $this->selectedSolrCoreConnection->getAdminService(); $rawSynonyms = $coreAdmin->getSynonyms(); foreach ($rawSynonyms as $baseWord => $synonymList) { $synonyms[$baseWord] = implode(', ', $synonymList); } $stopWords = $coreAdmin->getStopWords(); $this->view->assignMultiple([ 'synonyms' => $synonyms, 'stopWords' => implode(PHP_EOL, $stopWords), 'stopWordsCount' => count($stopWords) ]); } /** * Add synonyms to selected core * * @param string $baseWord * @param string $synonyms * @param bool $overrideExisting * @return void */ public function addSynonymsAction(string $baseWord, string $synonyms, $overrideExisting) { if (empty($baseWord) || empty($synonyms)) { $this->addFlashMessage( 'Please provide a base word and synonyms.', 'Missing parameter', FlashMessage::ERROR ); } else { $baseWord = mb_strtolower($baseWord); $synonyms = mb_strtolower($synonyms); $coreAdmin = $this->selectedSolrCoreConnection->getAdminService(); if ($overrideExisting && $coreAdmin->getSynonyms($baseWord)) { $coreAdmin->deleteSynonym($baseWord); } $coreAdmin->addSynonym($baseWord, GeneralUtility::trimExplode(',', $synonyms, true)); $coreAdmin->reloadCore(); $this->addFlashMessage( '"' . $synonyms . '" added as synonyms for base word "' . $baseWord . '"' ); } $this->redirect('index'); } /** * @param string $fileFormat * @return void */ public function exportStopWordsAction($fileFormat = 'txt') { $coreAdmin = $this->selectedSolrCoreConnection->getAdminService(); $this->exportFile( implode(PHP_EOL, $coreAdmin->getStopWords()), 'stopwords', $fileFormat ); } /** * Exports synonyms to a download file. * * @param string $fileFormat * @return string */ public function exportSynonymsAction($fileFormat = 'txt') { $coreAdmin = $this->selectedSolrCoreConnection->getAdminService(); $synonyms = $coreAdmin->getSynonyms(); return $this->exportFile(ManagedResourcesUtility::exportSynonymsToTxt($synonyms), 'synonyms', $fileFormat); } /** * @param array $synonymFileUpload * @param bool $overrideExisting * @param bool $deleteSynonymsBefore * @return void */ public function importSynonymListAction(array $synonymFileUpload, $overrideExisting, $deleteSynonymsBefore) { if ($deleteSynonymsBefore) { $this->deleteAllSynonyms(); } $fileLines = ManagedResourcesUtility::importSynonymsFromPlainTextContents($synonymFileUpload); $synonymCount = 0; $coreAdmin = $this->selectedSolrCoreConnection->getAdminService(); foreach ($fileLines as $baseWord => $synonyms) { if (!isset($baseWord) || empty($synonyms)) { continue; } $this->deleteExistingSynonym($overrideExisting, $deleteSynonymsBefore, $baseWord); $coreAdmin->addSynonym($baseWord, $synonyms); $synonymCount++; } $coreAdmin->reloadCore(); $this->addFlashMessage( $synonymCount . ' synonyms imported.' ); $this->redirect('index'); } /** * @param array $stopwordsFileUpload * @param bool $replaceStopwords * @return void */ public function importStopWordListAction(array $stopwordsFileUpload, $replaceStopwords) { $this->saveStopWordsAction( ManagedResourcesUtility::importStopwordsFromPlainTextContents($stopwordsFileUpload), $replaceStopwords ); } /** * Delete complete synonym list * * @return void */ public function deleteAllSynonymsAction() { $allSynonymsCouldBeDeleted = $this->deleteAllSynonyms(); $coreAdmin = $this->selectedSolrCoreConnection->getAdminService(); $reloadResponse = $coreAdmin->reloadCore(); if ($allSynonymsCouldBeDeleted && $reloadResponse->getHttpStatus() == 200 ) { $this->addFlashMessage( 'All synonym removed.' ); } else { $this->addFlashMessage( 'Failed to remove all synonyms.', 'An error occurred', FlashMessage::ERROR ); } $this->redirect('index'); } /** * Deletes a synonym mapping by its base word. * * @param string $baseWord Synonym mapping base word */ public function deleteSynonymsAction($baseWord) { $coreAdmin = $this->selectedSolrCoreConnection->getAdminService(); $deleteResponse = $coreAdmin->deleteSynonym($baseWord); $reloadResponse = $coreAdmin->reloadCore(); if ($deleteResponse->getHttpStatus() == 200 && $reloadResponse->getHttpStatus() == 200 ) { $this->addFlashMessage( 'Synonym removed.' ); } else { $this->addFlashMessage( 'Failed to remove synonym.', 'An error occurred', FlashMessage::ERROR ); } $this->redirect('index'); } /** * Saves the edited stop word list to Solr * * @param string $stopWords * @param bool $replaceStopwords * @return void */ public function saveStopWordsAction(string $stopWords, $replaceStopwords = true) { // lowercase stopword before saving because terms get lowercased before stopword filtering $newStopWords = mb_strtolower($stopWords); $newStopWords = GeneralUtility::trimExplode("\n", $newStopWords, true); $coreAdmin = $this->selectedSolrCoreConnection->getAdminService(); $oldStopWords = $coreAdmin->getStopWords(); if ($replaceStopwords) { $removedStopWords = array_diff($oldStopWords, $newStopWords); $wordsRemoved = $this->removeStopsWordsFromIndex($removedStopWords); } else { $wordsRemoved = true; } $wordsAdded = true; $addedStopWords = array_diff($newStopWords, $oldStopWords); if (!empty($addedStopWords)) { $wordsAddedResponse = $coreAdmin->addStopWords($addedStopWords); $wordsAdded = ($wordsAddedResponse->getHttpStatus() == 200); } $reloadResponse = $coreAdmin->reloadCore(); if ($wordsRemoved && $wordsAdded && $reloadResponse->getHttpStatus() == 200) { $this->addFlashMessage( 'Stop Words Updated.' ); } $this->redirect('index'); } /** * @param string $content * @param string $type * @param string $fileExtension * @return string */ protected function exportFile($content, $type = 'synonyms', $fileExtension = 'txt') : string { $coreAdmin = $this->selectedSolrCoreConnection->getAdminService(); $this->response->setHeader('Content-type', 'text/plain', true); $this->response->setHeader('Cache-control', 'public', true); $this->response->setHeader('Content-Description', 'File transfer', true); $this->response->setHeader( 'Content-disposition', 'attachment; filename =' . $type . '_' . $coreAdmin->getPrimaryEndpoint()->getCore() . '.' . $fileExtension, true ); $this->response->setContent($content); $this->sendFileResponse(); } /** * This method send the headers and content and does an exit, since without the exit TYPO3 produces and error. * @return void */ protected function sendFileResponse() { $this->response->sendHeaders(); $this->response->send(); $this->response->shutdown(); exit(); } /** * Delete complete synonym list form solr * * @return bool */ protected function deleteAllSynonyms() : bool { $coreAdmin = $this->selectedSolrCoreConnection->getAdminService(); $synonyms = $coreAdmin->getSynonyms(); $allSynonymsCouldBeDeleted = true; foreach ($synonyms as $baseWord => $synonym) { $deleteResponse = $coreAdmin->deleteSynonym($baseWord); $allSynonymsCouldBeDeleted = $allSynonymsCouldBeDeleted && $deleteResponse->getHttpStatus() == 200; } return $allSynonymsCouldBeDeleted; } /** * @param $stopwordsToRemove * @return bool */ protected function removeStopsWordsFromIndex($stopwordsToRemove) : bool { $wordsRemoved = true; $coreAdmin = $this->selectedSolrCoreConnection->getAdminService(); foreach ($stopwordsToRemove as $word) { $response = $coreAdmin->deleteStopWord($word); if ($response->getHttpStatus() != 200) { $wordsRemoved = false; $this->addFlashMessage( 'Failed to remove stop word "' . $word . '".', 'An error occurred', FlashMessage::ERROR ); break; } } return $wordsRemoved; } /** * Delete synonym entry if selceted before * @param bool $overrideExisting * @param bool $deleteSynonymsBefore * @param string $baseWord */ protected function deleteExistingSynonym($overrideExisting, $deleteSynonymsBefore, $baseWord) { $coreAdmin = $this->selectedSolrCoreConnection->getAdminService(); if (!$deleteSynonymsBefore && $overrideExisting && $coreAdmin->getSynonyms($baseWord) ) { $coreAdmin->deleteSynonym($baseWord); } } }