+
+
+
@@ -128,3 +132,5 @@
diff --git a/Classes/Common/PageTitleProvider.php b/Classes/Common/PageTitleProvider.php
new file mode 100644
index 0000000..589aa17
--- /dev/null
+++ b/Classes/Common/PageTitleProvider.php
@@ -0,0 +1,16 @@
+title = $title;
+ }
+}
diff --git a/Classes/Controller/SearchController.php b/Classes/Controller/SearchController.php
index 4c4fd63..e3f928e 100644
--- a/Classes/Controller/SearchController.php
+++ b/Classes/Controller/SearchController.php
@@ -12,90 +12,104 @@
use TYPO3\CMS\Core\Utility\GeneralUtility;
use Elastic\Elasticsearch\Exception\ClientResponseException;
use Elastic\Transport\Exception\RuntimeException;
-
-// ToDo:
-// Organize the transfer of the necessary parameters (index name, fields, etc.) from the other extensions (ExtensionConfiguration?) -> see in ElasticSearchServic
-// Elastic Search Index return standardized fields? Standardized search fields or own params from the respective extension?
-// process search parameters from the URL query parameters to search
+use TYPO3\CMS\Core\MetaTag\MetaTagManagerRegistry;
+use Slub\LisztCommon\Common\PageTitleProvider;
+use TYPO3\CMS\Core\Page\AssetCollector;
+use TYPO3\CMS\Core\Cache\CacheManager;
+use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
final class SearchController extends ClientEnabledController
{
// set resultLimit as intern variable from $this->settings['resultLimit'];
protected int $resultLimit;
+ private FrontendInterface $runtimeCache;
// Dependency Injection of Repository
// https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/DependencyInjection/Index.html#Dependency-Injection
-
public function __construct(
private readonly ElasticSearchServiceInterface $elasticSearchService,
- protected ExtensionConfiguration $extConf
- )
- {
+ protected ExtensionConfiguration $extConf,
+ private readonly PageTitleProvider $titleProvider,
+ private readonly AssetCollector $assetCollector,
+ CacheManager $cacheManager,
+ ) {
$this->resultLimit = $this->settings['resultLimit'] ?? 25;
+ $this->runtimeCache = $cacheManager->getCache('runtime');
}
public function indexAction(array $searchParams = []): ResponseInterface
{
- $language = $this->request->getAttribute('language');
- $locale = $language->getLocale();
- if (
- isset($searchParams['page']) &&
- (int) $searchParams['page'] > 0
- ) {
- $currentPage = (int) $searchParams['page'];
- } else {
- $currentPage = 1;
- }
-
- // $totalItems = $this->elasticSearchService->count($searchParams, $this->settings);
- //$totalItems = 100;
+ $locale = $this->request->getAttribute('language')->getLocale();
+ $currentPage = $this->getCurrentPage($searchParams);
+ $this->addViewTransitionStyle();
$elasticResponse = $this->elasticSearchService->search($searchParams, $this->settings);
- $paginator = (new Paginator())->
- setPage($currentPage)->
- setTotalItems($elasticResponse['hits']['total']['value'])->
- setExtensionConfiguration($this->extConf);
- $pagination = $paginator->getPagination();
- $showPagination = $paginator->getTotalPages() > 1 ? true : false;
-
- $this->view->assign('locale', $locale);
- $this->view->assign('totalItems', $elasticResponse['hits']['total']['value']);
- $this->view->assign('searchParams', $searchParams);
- $this->view->assign('searchResults', $elasticResponse);
- $this->view->assign('pagination', $pagination);
- $this->view->assign('showPagination', $showPagination);
- // $this->view->assign('totalItems', $totalItems);
- $this->view->assign('currentString', Paginator::CURRENT_PAGE);
- $this->view->assign('dots', Paginator::DOTS);
- $this->view->assign('detailPageId', $this->extConf->get('liszt_common','detailPageId'));
+ $totalItems = 0;
+ if (isset($elasticResponse['hits'], $elasticResponse['hits']['total'], $elasticResponse['hits']['total']['value'])) {
+ $totalItems = (int)$elasticResponse['hits']['total']['value'];
+ }
+ $paginator = (new Paginator())
+ ->setPage($currentPage)
+ ->setTotalItems($totalItems)
+ ->setExtensionConfiguration($this->extConf);
+ $pagination = $paginator->getPagination();
+ $showPagination = $paginator->getTotalPages() > 1;
+
+ $this->view->assignMultiple([
+ 'locale' => $locale,
+ 'totalItems' => $totalItems,
+ 'searchParams' => $searchParams,
+ 'searchResults' => $elasticResponse,
+ 'pagination' => $pagination,
+ 'showPagination'=> $showPagination,
+ 'currentString' => Paginator::CURRENT_PAGE,
+ 'dots' => Paginator::DOTS,
+ 'detailPageId' => $this->extConf->get('liszt_common', 'detailPageId'),
+ ]);
return $this->htmlResponse();
}
+
public function searchBarAction(array $searchParams = []): ResponseInterface
{
$this->view->assign('searchParams', $searchParams);
return $this->htmlResponse();
}
- public function detailsAction(array $searchParams = []): ResponseInterface
+
+ public function detailsHeaderAction(array $searchParams = []): ResponseInterface
{
+ $documentId = $this->getDocumentIdFromRouting();
+ if (!$documentId) {
+ return $this->redirectToNotFoundPage();
+ }
+
+ try {
+ $elasticResponse = $this->loadDetailPageFromElastic($documentId);
+ } catch (ClientResponseException $e) {
+ if ($e->getCode() === 404) {
+ return $this->redirectToNotFoundPage();
+ }
+ throw $e;
+ }
+
+ $this->view->assign('searchResult', $elasticResponse);
+ return $this->htmlResponse();
+ }
- $routing = $this->request->getAttribute('routing');
- $routingArgs = $routing->getArguments();
- // Check if 'tx_lisztcommon_searchdetails' exists and if 'detailId' has a valid value.
- $documentId = null;
- if (!empty($routingArgs['tx_lisztcommon_searchdetails']['documentId'])) {
- $documentId = $routingArgs['tx_lisztcommon_searchdetails']['documentId'];
- } else {
+ public function detailsAction(array $searchParams = []): ResponseInterface
+ {
+ $documentId = $this->getDocumentIdFromRouting();
+ if (!$documentId) {
return $this->redirectToNotFoundPage();
}
try {
- $elasticResponse = $this->elasticSearchService->getDocumentById($documentId, []);
+ $elasticResponse = $this->loadDetailPageFromElastic($documentId);
} catch (ClientResponseException $e) {
// Handle 404 errors
if ($e->getCode() === 404) {
@@ -105,25 +119,82 @@ public function detailsAction(array $searchParams = []): ResponseInterface
}
- $this->view->assign('searchParams', $searchParams);
- $this->view->assign('routingArgs', $routingArgs);
- $this->view->assign('detailId', $documentId);
- $this->view->assign('searchResult', $elasticResponse);
- $this->view->assign('detailPageId', $this->extConf->get('liszt_common','detailPageId'));
+ // set page title
+ $pageTitle = $this->formatPageTitle($elasticResponse['_source']['title'] ?? 'Details');
+ $this->titleProvider->setTitle($pageTitle);
+
+ // set meta description
+ $metaDescription = '';
+ if (isset($elasticResponse['_source'], $elasticResponse['_source']['tx_lisztcommon_searchable'])) {
+ $metaDescription = (string)$elasticResponse['_source']['tx_lisztcommon_searchable'];
+ }
+ $metaTagManager = GeneralUtility::makeInstance(MetaTagManagerRegistry::class)
+ ->getManagerForProperty('description');
+ $metaTagManager->addProperty('description', $metaDescription);
+
+ $this->addViewTransitionStyle();
+
+ $this->view->assignMultiple([
+ 'searchParams' => $searchParams,
+ 'routingArgs' => $this->request->getAttribute('routing')->getArguments(),
+ 'detailId' => $documentId,
+ 'searchResult' => $elasticResponse,
+ 'detailPageId' => $this->extConf->get('liszt_common', 'detailPageId'),
+ ]);
return $this->htmlResponse();
+ }
+
+ private function getCurrentPage(array $params): int
+ {
+ return (isset($params['page']) && (int)$params['page'] > 0) ? (int)$params['page'] : 1;
+ }
+ // add view transition styles as inline style for animation
+ private function addViewTransitionStyle(): void
+ {
+ $this->assetCollector->addInlineStyleSheet(
+ 'view-transitions-root',
+ '@media screen and (prefers-reduced-motion: no-preference) { @view-transition { navigation: auto; } }'
+ );
}
+ private function getDocumentIdFromRouting(): ?string
+ {
+ $routingArgs = $this->request->getAttribute('routing')->getArguments();
+ return $routingArgs['tx_lisztcommon_searchdetails']['documentId'] ?? null;
+ }
+
+ private function formatPageTitle(string $title): string
+ {
+ $maxTitleLength = 50;
+ return (mb_strlen($title) > $maxTitleLength)
+ ? mb_substr($title, 0, $maxTitleLength - 3) . '...'
+ : $title;
+ }
+
+ private function loadDetailPageFromElastic(string $documentId): Collection
+ {
+ $cachedResult = $this->runtimeCache->get($documentId);
+ if (!$cachedResult instanceof Collection) {
+ $result = $this->elasticSearchService->getDocumentById($documentId, []);
+ if (!$result instanceof Collection) {
+ throw new \UnexpectedValueException("The return value of getDocumentById does not correspond to the expected collection type."); }
+ $this->runtimeCache->set($documentId, $result);
+ return $result;
+ }
+ return $cachedResult;
+ }
+
+
public function redirectToNotFoundPage(): ResponseInterface
{
- // see: https://docs.typo3.org/m/typo3/reference-coreapi/12.4/en-us/ExtensionArchitecture/Extbase/Reference/Controller/ActionController.html
+ // see: https://docs.typo3.org/m/typo3/reference-coreapi/12.4/en-us/ExtensionArchitecture/Extbase/Reference/Controller/ActionController.html
// $uri could also be https://example.com/any/uri
// $this->resourceFactory is injected as part of the `ActionController` inheritance
return $this->responseFactory->createResponse(301)
->withHeader('Location', '/404/'); // the uri of the 404 page from typo installation
}
-
}
diff --git a/Classes/Event/Listener/SiteConfigurationLoadedEventListener.php b/Classes/Event/Listener/SiteConfigurationLoadedEventListener.php
new file mode 100644
index 0000000..686913b
--- /dev/null
+++ b/Classes/Event/Listener/SiteConfigurationLoadedEventListener.php
@@ -0,0 +1,25 @@
+getConfiguration();
+ $fileLoader = GeneralUtility::makeInstance(YamlFileLoader::class);
+ $routeEnhancersConfiguration = $fileLoader->load('EXT:liszt_common/Configuration/Routing/routeEnhancers.yaml');
+ ArrayUtility::mergeRecursiveWithOverrule($configuration, $routeEnhancersConfiguration);
+ $event->setConfiguration($configuration);
+ }
+
+}
diff --git a/Classes/ViewHelpers/GetDetailBreadCrumbArrayViewHelper.php b/Classes/ViewHelpers/GetDetailBreadCrumbArrayViewHelper.php
new file mode 100644
index 0000000..548cc4a
--- /dev/null
+++ b/Classes/ViewHelpers/GetDetailBreadCrumbArrayViewHelper.php
@@ -0,0 +1,23 @@
+getRequest();$frontendController = $request->getAttribute('frontend.controller');
+ $rootline = $frontendController->rootLine;
+ return array_reverse($rootline);
+ }
+}
diff --git a/Classes/Routing/Configuration/config.yaml b/Configuration/Routing/routeEnhancers.yaml
similarity index 100%
rename from Classes/Routing/Configuration/config.yaml
rename to Configuration/Routing/routeEnhancers.yaml
diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml
index 5ce66db..afc2520 100644
--- a/Configuration/Services.yaml
+++ b/Configuration/Services.yaml
@@ -6,5 +6,14 @@ services:
Slub\LisztCommon\:
resource: '../Classes/*'
exclude: '../Classes/Domain/Model/*'
+ Slub\LisztCommon\Event\Listener\SiteConfigurationLoadedEventListener:
+ tags:
+ - name: event.listener
+ method: modify
+ identifier: 'ext-lisztcommon/site-configuration-loaded'
+ event: TYPO3\CMS\Core\Configuration\Event\SiteConfigurationLoadedEvent
+
+
+
# if more than one! class implement the same interface (i.e.Search Interface) read: https://usetypo3.com/dependency-injection/
# and https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/DependencyInjection/Index.html
diff --git a/Configuration/TCA/Overrides/tt_content.php b/Configuration/TCA/Overrides/tt_content.php
index cd6c19d..c90b05a 100644
--- a/Configuration/TCA/Overrides/tt_content.php
+++ b/Configuration/TCA/Overrides/tt_content.php
@@ -26,6 +26,13 @@
'Liszt Search Details'
);
+
+ExtensionUtility::registerPlugin(
+ 'liszt_common',
+ 'SearchDetailsHeader',
+ 'Liszt Search Details Header'
+);
+
// Adds the content element to the "Type" dropdown
ExtensionManagementUtility::addTcaSelectItem(
'tt_content',
diff --git a/Configuration/TypoScript/constants.typoscript b/Configuration/TypoScript/constants.typoscript
index 405f315..604c114 100644
--- a/Configuration/TypoScript/constants.typoscript
+++ b/Configuration/TypoScript/constants.typoscript
@@ -9,12 +9,4 @@
}*/
-/*
-plugin.tx_liszt_common {
-view {
-templateRootPath = EXT:liszt_common/Resources/Private/Templates/
-partialRootPath = EXT:liszt_common/Resources/Private/Partials/
-layoutRootPath = EXT:liszt_common/Resources/Private/Layouts/
-}
-}*/
diff --git a/Configuration/TypoScript/setup.typoscript b/Configuration/TypoScript/setup.typoscript
index 1b8ab80..646257d 100644
--- a/Configuration/TypoScript/setup.typoscript
+++ b/Configuration/TypoScript/setup.typoscript
@@ -17,10 +17,18 @@ page {
partialRootPaths {
20 = EXT:liszt_common/Resources/Private/Partials/
}
+ layoutRootPaths {
+ 20 = EXT:liszt_common/Resources/Private/Layouts/
+ }
+
}
+
+// 20 < tt_content.list.20.lisztcommon_searchdetails
+
}
+
lib.contentElement {
templateRootPaths {
200 = EXT:liszt_common/Resources/Private/ExtensionOverrides/fluid_styled_content/Templates/
@@ -54,6 +62,15 @@ plugin.tx_lisztcommon_searchlisting.view.pluginNamespace = tx_liszt_common_searc
plugin.tx_lisztcommon_searchbar.mvc.callDefaultActionIfActionCantBeResolved = 1;
+// set own pageTitlePovider
+config {
+ pageTitleProviders {
+ liszt_common {
+ provider = Slub\LisztCommon\Common\PageTitleProvider
+ before = record
+ }
+ }
+}
# get the selected frontend layout from page table for show/hide SearchBar, because bootstrap package not use this param
#lib.selectedFrontendLayout = TEXT
@@ -61,6 +78,17 @@ plugin.tx_lisztcommon_searchbar.mvc.callDefaultActionIfActionCantBeResolved = 1;
+// Partial Root Path to find logo and other Partials from liszt_web
+/*plugin.tx_lisztcommon_searchdetails {
+ view {
+ partialRootPaths {
+ 10 = EXT:liszt_common/Resources/Private/Partials/
+ 20 = EXT:liszt_web/Resources/Private/Partials/
+ }
+ }
+}
+*/
+
# copy element in PAGE object to display in fluid template
# page = PAGE
/*page {
diff --git a/Resources/Private/Language/de.locallang.xlf b/Resources/Private/Language/de.locallang.xlf
index 5e85920..7b61673 100644
--- a/Resources/Private/Language/de.locallang.xlf
+++ b/Resources/Private/Language/de.locallang.xlf
@@ -72,6 +72,9 @@
+
+
+
{searchResult._source.tx_lisztcommon_header}
+