Apache Solr 10 runs on a dedicated VPS behind a Caddy HTTPS proxy (vitecsolr.evomedien.de) because the managed webserver only allows outgoing standard ports. Credentials stay out of git: the site config carries %env()% placeholders resolved via putenv() in the git-ignored config/system/additional.php. - composer: apache-solr-for-typo3/solr 14.0.0-RC1 (the only line compatible with TYPO3 14; final 14.0.0 will arrive via composer update) - config.yaml: read connection https/443, core_en per language, env placeholder credentials; .gitignore covers additional.php - setup.typoscript: config.index_enable = 1 (page indexing silently refuses without it), solr_pi_results JSON renderer registration, results highlighting, and content field extraction from tt_content rows - the headless JSON output has no TYPO3SEARCH markers, so the default page content extraction indexed an empty content field - SearchJsonRenderer (renderer #27): JSON output for the search plugin on /search. GET q/page in; { query, page, resultsPerPage, numFound, totalPages, results[], suggestions } out. Disables the page cache per request: config.no_cache is gone in TYPO3 v14, and q/page are excluded from cHash, so cached variants would collide - ext_localconf.php: q and page added to cacheHash excludedParameters - SolrIndexCommand (vitec:solr-index): works the index queue from the CLI with connection diagnostics and a --debug single-step mode - EXT:solr 14 ships no console commands and the backend button indexes one item per click
339 lines
10 KiB
Plaintext
Executable File
339 lines
10 KiB
Plaintext
Executable File
config {
|
|
# EXT:solr indexes pages only when indexing is explicitly enabled
|
|
index_enable = 1
|
|
pageTitleProviders {
|
|
vitec {
|
|
provider = Evomedien\Vitec\PageTitle\ProductPageTitleProvider
|
|
before = record
|
|
before = seo
|
|
}
|
|
}
|
|
}
|
|
|
|
@import 'EXT:headless/Configuration/TypoScript/ContentElement/*.typoscript'
|
|
|
|
# =============================================================================
|
|
# Headless list-plugin JSON renderers (TYPO3 v14: each plugin is its own CType)
|
|
#
|
|
# For every VITEC plugin the renderer's USER output is exposed as a sub-field
|
|
# of the standard content-element envelope (`content.<key>`), inheriting all
|
|
# default fields (id, type, colPos, categories, appearance, …) from
|
|
# lib.contentElement provided by friendsoftypo3/headless.
|
|
# =============================================================================
|
|
|
|
tt_content {
|
|
vitec_productlist < lib.contentElementWithHeader
|
|
vitec_productlist {
|
|
fields {
|
|
content {
|
|
fields {
|
|
products = USER
|
|
products.userFunc = Evomedien\Vitec\UserFunc\ProductListJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# EXT:solr search results as JSON - the search endpoint for the React frontend
|
|
solr_pi_results < lib.contentElementWithHeader
|
|
solr_pi_results {
|
|
fields {
|
|
content {
|
|
fields {
|
|
search = USER
|
|
search.userFunc = Evomedien\Vitec\UserFunc\SearchJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
news_pi1 < lib.contentElementWithHeader
|
|
news_pi1 {
|
|
fields {
|
|
content {
|
|
fields {
|
|
news = USER
|
|
news.userFunc = Evomedien\Vitec\UserFunc\NewsJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
news_newsliststicky < tt_content.news_pi1
|
|
news_newsselectedlist < tt_content.news_pi1
|
|
news_newsdetail < tt_content.news_pi1
|
|
news_newsdatemenu < tt_content.news_pi1
|
|
news_categorylist < tt_content.news_pi1
|
|
news_newssearchform < tt_content.news_pi1
|
|
news_newssearchresult < tt_content.news_pi1
|
|
news_taglist < tt_content.news_pi1
|
|
|
|
vitec_productshow < lib.contentElementWithHeader
|
|
vitec_productshow {
|
|
fields {
|
|
content {
|
|
fields {
|
|
product = USER
|
|
product.userFunc = Evomedien\Vitec\UserFunc\ProductShowJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vitec_usecaselist < lib.contentElementWithHeader
|
|
vitec_usecaselist {
|
|
fields {
|
|
content {
|
|
fields {
|
|
usecases = USER
|
|
usecases.userFunc = Evomedien\Vitec\UserFunc\UsecaseListJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vitec_usecaseshow < lib.contentElementWithHeader
|
|
vitec_usecaseshow {
|
|
fields {
|
|
content {
|
|
fields {
|
|
usecase = USER
|
|
usecase.userFunc = Evomedien\Vitec\UserFunc\UsecaseShowJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vitec_marketshow < lib.contentElementWithHeader
|
|
vitec_marketshow {
|
|
fields {
|
|
content {
|
|
fields {
|
|
market = USER
|
|
market.userFunc = Evomedien\Vitec\UserFunc\MarketShowJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vitec_marketlist < lib.contentElementWithHeader
|
|
vitec_marketlist {
|
|
fields {
|
|
content {
|
|
fields {
|
|
markets = USER
|
|
markets.userFunc = Evomedien\Vitec\UserFunc\MarketListJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vitec_solutionlist < lib.contentElementWithHeader
|
|
vitec_solutionlist {
|
|
fields {
|
|
content {
|
|
fields {
|
|
solutions = USER
|
|
solutions.userFunc = Evomedien\Vitec\UserFunc\SolutionListJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vitec_solutionshow < lib.contentElementWithHeader
|
|
vitec_solutionshow {
|
|
fields {
|
|
content {
|
|
fields {
|
|
solution = USER
|
|
solution.userFunc = Evomedien\Vitec\UserFunc\SolutionShowJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vitec_downloadcard < lib.contentElementWithHeader
|
|
vitec_downloadcard {
|
|
fields {
|
|
content {
|
|
fields {
|
|
downloadcard = USER
|
|
downloadcard.userFunc = Evomedien\Vitec\UserFunc\DownloadcardJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vitec_downloadcardcollection < lib.contentElementWithHeader
|
|
vitec_downloadcardcollection {
|
|
fields {
|
|
content {
|
|
fields {
|
|
downloadcardcollection = USER
|
|
downloadcardcollection.userFunc = Evomedien\Vitec\UserFunc\DownloadcardcollectionJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vitec_datasheets < lib.contentElementWithHeader
|
|
vitec_datasheets {
|
|
fields {
|
|
content {
|
|
fields {
|
|
datasheets = USER
|
|
datasheets.userFunc = Evomedien\Vitec\UserFunc\DatasheetsJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vitec_eventlist =< lib.contentElementWithHeader
|
|
vitec_eventlist {
|
|
fields {
|
|
content {
|
|
fields {
|
|
eventlist = USER
|
|
eventlist.userFunc = Evomedien\Vitec\UserFunc\EventlistJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vitec_contactform < lib.contentElementWithHeader
|
|
vitec_contactform {
|
|
fields {
|
|
content {
|
|
fields {
|
|
form = USER
|
|
form.userFunc = Evomedien\Vitec\UserFunc\FormsJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vitec_demoform < tt_content.vitec_contactform
|
|
vitec_helpdeskform < tt_content.vitec_contactform
|
|
|
|
vitec_modelcard < lib.contentElementWithHeader
|
|
vitec_modelcard {
|
|
fields {
|
|
content {
|
|
fields {
|
|
card = USER
|
|
card.userFunc = Evomedien\Vitec\UserFunc\ModelcardJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vitec_customerlogos < lib.contentElementWithHeader
|
|
vitec_customerlogos {
|
|
fields {
|
|
content {
|
|
fields {
|
|
customerlogos = USER
|
|
customerlogos.userFunc = Evomedien\Vitec\UserFunc\CustomerlogosJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vitec_locationlist < lib.contentElementWithHeader
|
|
vitec_locationlist {
|
|
fields {
|
|
content {
|
|
fields {
|
|
locations = USER
|
|
locations.userFunc = Evomedien\Vitec\UserFunc\LocationsJsonRenderer->render
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Include container (layout) rendering definitions
|
|
@import 'EXT:vitec/Configuration/TypoScript/Headless/vitec_containers.typoscript'
|
|
|
|
# Include menu (navigation) JSON definitions
|
|
@import 'EXT:vitec/Configuration/TypoScript/Headless/vitec_menus.typoscript'
|
|
|
|
# =============================================================================
|
|
# Page-level structured data (schema.org JSON-LD @graph)
|
|
#
|
|
# Organization + WebSite + BreadcrumbList + NewsArticle, auto-generated server
|
|
# side from page/site data. The headless frontend wraps the returned string in
|
|
# <script type="application/ld+json"> inside the page <head>.
|
|
# =============================================================================
|
|
page.10.fields.jsonLd = USER
|
|
page.10.fields.jsonLd.userFunc = Evomedien\Vitec\UserFunc\PageJsonLdRenderer->render
|
|
|
|
############################################################
|
|
# Frontend resolution for the record links from the link
|
|
# browser (page.tsconfig TCEMAIN.linkHandler):
|
|
# download -> forced download endpoint (DownloadFileMiddleware)
|
|
# product -> detail view, same /product/<slug> convention as
|
|
# the `link` field in the product JSON (page 10)
|
|
# A hidden or deleted target makes the typolink fail -> null.
|
|
############################################################
|
|
|
|
config.recordLinks {
|
|
download {
|
|
forceLink = 1
|
|
typolink {
|
|
parameter.cObject = TEXT
|
|
parameter.cObject.dataWrap = /download/file/{field:uid}
|
|
}
|
|
}
|
|
product {
|
|
forceLink = 1
|
|
typolink {
|
|
parameter.cObject = TEXT
|
|
parameter.cObject.dataWrap = /product/{field:slug}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Highlighted teaser fragments for the JSON search results (SearchJsonRenderer)
|
|
plugin.tx_solr.search.results.resultsHighlighting = 1
|
|
plugin.tx_solr.search.results.resultsHighlighting.wrap = <mark>|</mark>
|
|
|
|
|
|
# Headless has no TYPO3SEARCH_begin/end markers in its JSON output, so the
|
|
# default page content extraction indexes an EMPTY content field. Build the
|
|
# content from the page's tt_content rows instead: header, subheader, bodytext
|
|
# cover core CTypes and the Content Blocks (their text fields reuse bodytext),
|
|
# vitec_quote is the only Content Blocks text column of its own. SOLR_CONTENT
|
|
# strips markup before indexing. Collection children (FAQ items, cards) live in
|
|
# their own tables and are not covered yet.
|
|
plugin.tx_solr.index.queue.pages.fields {
|
|
content = SOLR_CONTENT
|
|
content {
|
|
cObject = COA
|
|
cObject {
|
|
10 = CONTENT
|
|
10 {
|
|
table = tt_content
|
|
select {
|
|
orderBy = sorting
|
|
}
|
|
renderObj = COA
|
|
renderObj {
|
|
10 = TEXT
|
|
10.field = header
|
|
10.noTrimWrap = || |
|
|
20 = TEXT
|
|
20.field = subheader
|
|
20.noTrimWrap = || |
|
|
30 = TEXT
|
|
30.field = bodytext
|
|
30.noTrimWrap = || |
|
|
40 = TEXT
|
|
40.field = vitec_quote
|
|
40.noTrimWrap = || |
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|