Files
VITEC-website/packages/vitec/Configuration/Sets/Vitecset/setup.typoscript
T
orasche 91ad5e6596 Search cards above the results, detail-page fallback for market/solution documents
- New record type tx_vitec_domain_model_searchcard ("best bets"):
  keyword list, priority, optional referenced record (page, product,
  story, solution, market, news) and overridable title/teaser/image/link;
  hidden/start/end time allow timed campaigns
- SearchCardService delivers content.search.cards on page 1: max. 3,
  priority-ordered, deduplicated by URL, independent of filter, facets
  and result count. Keywords match as whole words/phrases, case- and
  punctuation-insensitive, including one level of the Solr managed
  synonyms in both directions (cached for an hour, fail-soft). Record
  cards read the record's indexed Solr document, so a card always
  matches the record's own search result
- Index fallback for markets and solutions: when the record carries no
  image or text, the document takes the intro image (then the hero
  background) and the hero subline, intro headline and intro text from
  the content blocks of its detail page - selected by CType, so it works
  for layout 21 and layout 5 alike; improves results and cards
- Fix: a search request without q returned an empty body instead of the
  documented empty shape (stdClass resolved inside the renderer namespace)
- migrations/check_solution_fields.php: shows live/workspace versions of
  solution records
- Spec v1.22 (clause 7.16: cards)
2026-10-09 12:59:12 +02:00

991 lines
33 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
}
vitecUsecase {
provider = Evomedien\Vitec\PageTitle\UsecasePageTitleProvider
before = record
before = seo
}
}
}
@import 'EXT:headless/Configuration/TypoScript/ContentElement/*.typoscript'
# =============================================================================
# header_layout 100 = "Hidden" (decision 2026-09-04): such headers stay OUT of
# the JSON - header, subheader and headerLink render empty. headerLayout (100)
# stays in the payload so the front end could still tell why. Placed BEFORE
# the `< lib.contentElementWithHeader` copies below, which snapshot this lib.
# The content-blocks `data` path gets the same rule via HiddenHeaderProcessor.
# =============================================================================
lib.contentElementWithHeader.fields.content.fields {
header.stdWrap.if {
value = 100
equals.field = header_layout
negate = 1
}
subheader.stdWrap.if {
value = 100
equals.field = header_layout
negate = 1
}
headerLink.stdWrap.if {
value = 100
equals.field = header_layout
negate = 1
}
}
lib.contentBlock.fields.data.dataProcessing.20 = Evomedien\Vitec\DataProcessing\HiddenHeaderProcessor
# =============================================================================
# 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
}
}
}
}
# Featured product card (sub-market template section 5): one referenced
# product as a full product card under `productfeature` - same shape as
# the list items, serialization shared with ProductListJsonRenderer.
vitec_productfeature < lib.contentElementWithHeader
vitec_productfeature {
fields {
content {
fields {
productfeature = USER
productfeature.userFunc = Evomedien\Vitec\UserFunc\ProductFeatureJsonRenderer->render
}
}
}
}
# Product Finder - only the category hierarchy for the filter selects.
# Placed once above the product lists; the front end filters the products
# those lists already delivered, client side, via ?cat= and ?subcat=.
vitec_productfinder < lib.contentElementWithHeader
vitec_productfinder {
fields {
content {
fields {
productfinder = USER
productfinder.userFunc = Evomedien\Vitec\UserFunc\ProductFinderJsonRenderer->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 = || |
# Content Blocks with their own text columns (market/solution
# templates, flip cards) - the workbook imports fill these
50 = TEXT
50.field = vitec_subline
50.noTrimWrap = || |
60 = TEXT
60.field = vitec_intro_headline
60.noTrimWrap = || |
70 = TEXT
70.field = vitec_intro_text
70.noTrimWrap = || |
80 = TEXT
80.field = vitec_text1
80.noTrimWrap = || |
90 = TEXT
90.field = vitec_text2
90.noTrimWrap = || |
100 = TEXT
100.field = vitec_back_text
100.noTrimWrap = || |
110 = TEXT
110.field = vitec_quote_position
110.noTrimWrap = || |
# Content Blocks collection children (cards, icon lists,
# FAQ) live in their own tables, keyed on the parent uid
120 = CONTENT
120 {
table = vitec_items
select {
pidInList.field = pid
where.dataWrap = {#foreign_table_parent_uid} = {field:uid}
orderBy = sorting
}
renderObj = COA
renderObj {
10 = TEXT
10.field = headline
10.noTrimWrap = || |
20 = TEXT
20.field = text
20.noTrimWrap = || |
}
}
130 = CONTENT
130 {
table = vitec_faq_items
select {
pidInList.field = pid
where.dataWrap = {#foreign_table_parent_uid} = {field:uid}
orderBy = sorting
}
renderObj = COA
renderObj {
10 = TEXT
10.field = question
10.noTrimWrap = || |
20 = TEXT
20.field = answer
20.noTrimWrap = || |
}
}
}
}
}
}
}
# Log every executed search (keywords, hit count) into tx_solr_statistics -
# feeds the Search Phrase Statistics backend module, most useful for the
# zero-hit list. anonymizeIP masks the last two octets before storing (GDPR).
plugin.tx_solr.statistics = 1
plugin.tx_solr.statistics.anonymizeIP = 2
# =============================================================================
# Record indexing: custom domain models as their own search documents.
# The configuration name becomes the document `type` in the search JSON, so the
# frontend can facet/label results. Every type needs a resolvable `url` -
# a hit without a link is useless, hence the where-clauses below.
# NOT indexed (yet): solutions (no slug, no detail_page - nothing to link to),
# downloads/events/locations (no public detail pages), Datapath news (pid 442,
# import awaiting an editorial decision).
# =============================================================================
# Detail-page fallbacks for markets and solutions (2026-10-09). The records
# themselves are mostly empty (no teaser/description/image) - the real copy
# lives in the market/solution Content Blocks on the record's detail_page,
# filled by the workbook importers. Copied into both queues below, so search
# results AND search cards (which mirror the indexed document) get a teaser
# and a tile image without double maintenance. Selected by CType, so it works
# for layout 21 (colPos 110/120) and layout 5 (colPos 1/0) alike.
#
# Image chain: record image -> detail page intro image -> hero background.
lib.vitecIndexDetailImage = CONTENT
lib.vitecIndexDetailImage {
table = tt_content
select {
pidInList.field = detail_page
where = {#CType} = 'vitec_marketintro'
orderBy = sorting
max = 1
}
renderObj = FILES
renderObj {
references {
table = tt_content
uid.data = field:uid
fieldName = vitec_image
}
maxItems = 1
renderObj = IMG_RESOURCE
renderObj {
file.import.data = file:current:uid
file.treatIdAsReference = 1
file.width = 640
}
}
stdWrap.ifEmpty.cObject = CONTENT
stdWrap.ifEmpty.cObject {
table = tt_content
select {
pidInList.field = detail_page
where = {#CType} = 'vitec_markethero'
orderBy = sorting
max = 1
}
renderObj = FILES
renderObj {
references {
table = tt_content
uid.data = field:uid
fieldName = vitec_bgimage
}
maxItems = 1
renderObj = IMG_RESOURCE
renderObj {
file.import.data = file:current:uid
file.treatIdAsReference = 1
file.width = 640
}
}
}
}
# Searchable text of the detail page: hero subline, intro headline, intro
# text - in page order (hero first), appended to the record's own content.
lib.vitecIndexDetailText = CONTENT
lib.vitecIndexDetailText {
table = tt_content
select {
pidInList.field = detail_page
where = {#CType} IN ('vitec_markethero', 'vitec_marketintro')
orderBy = CType ASC
}
renderObj = COA
renderObj {
10 = TEXT
10.field = vitec_subline
10.noTrimWrap = || |
20 = TEXT
20.field = vitec_intro_headline
20.noTrimWrap = || |
30 = TEXT
30.field = vitec_intro_text
30.noTrimWrap = || |
}
}
plugin.tx_solr.index.queue {
products = 1
products {
# result tile image: first reference, processed to 640px width
fields.image = FILES
fields.image {
references {
table = tx_vitec_domain_model_product
uid.data = field:uid
fieldName = image
}
maxItems = 1
renderObj = IMG_RESOURCE
renderObj {
file.import.data = file:current:uid
file.treatIdAsReference = 1
file.width = 640
}
stdWrap.ifEmpty.cObject = FILES
stdWrap.ifEmpty.cObject {
references {
table = tx_vitec_domain_model_product
uid.data = field:uid
fieldName = productimage
}
maxItems = 1
renderObj = IMG_RESOURCE
renderObj {
file.import.data = file:current:uid
file.treatIdAsReference = 1
file.width = 640
}
}
}
# sys_category titles - feeds the category facet
fields.category_stringM = SOLR_RELATION
fields.category_stringM {
localField = categories
multiValue = 1
}
type = tx_vitec_domain_model_product
additionalWhereClause = hideonwebsite = 0
fields {
title = title
content = SOLR_CONTENT
content {
cObject = COA
cObject {
10 = TEXT
10.field = subtitle
10.noTrimWrap = || |
20 = TEXT
20.field = teaser
20.noTrimWrap = || |
30 = TEXT
30.field = description
30.noTrimWrap = || |
40 = TEXT
40.field = description2
40.noTrimWrap = || |
50 = TEXT
50.field = capabilities
50.noTrimWrap = || |
60 = TEXT
60.field = highlights
60.noTrimWrap = || |
70 = TEXT
70.field = applications
70.noTrimWrap = || |
}
}
url = TEXT
url {
typolink {
parameter = 10
additionalParams = &tx_vitec_productshow[product]={field:uid}
additionalParams.insertData = 1
returnLast = url
}
}
}
}
markets = 1
markets {
# result tile image: first reference, processed to 640px width
fields.image = FILES
fields.image {
references {
table = tx_vitec_domain_model_market
uid.data = field:uid
fieldName = image
}
maxItems = 1
renderObj = IMG_RESOURCE
renderObj {
file.import.data = file:current:uid
file.treatIdAsReference = 1
file.width = 640
}
stdWrap.ifEmpty.cObject < lib.vitecIndexDetailImage
}
type = tx_vitec_domain_model_market
# only markets with an editorially assigned detail page are linkable;
# grows automatically as editors fill the field (4 of 34 right now)
additionalWhereClause = detail_page > 0
fields {
title = title
content = SOLR_CONTENT
content {
cObject = COA
cObject {
10 = TEXT
10.field = subtitle
10.noTrimWrap = || |
20 = TEXT
20.field = teaser
20.noTrimWrap = || |
30 = TEXT
30.field = description
30.noTrimWrap = || |
40 < lib.vitecIndexDetailText
}
}
url = TEXT
url {
typolink {
parameter.field = detail_page
returnLast = url
}
}
}
}
usecases = 1
usecases {
# result tile image: first reference, processed to 640px width
fields.image = FILES
fields.image {
references {
table = tx_vitec_domain_model_usecase
uid.data = field:uid
fieldName = card_image
}
maxItems = 1
renderObj = IMG_RESOURCE
renderObj {
file.import.data = file:current:uid
file.treatIdAsReference = 1
file.width = 640
}
stdWrap.ifEmpty.cObject = FILES
stdWrap.ifEmpty.cObject {
references {
table = tx_vitec_domain_model_usecase
uid.data = field:uid
fieldName = hero_bgimage
}
maxItems = 1
renderObj = IMG_RESOURCE
renderObj {
file.import.data = file:current:uid
file.treatIdAsReference = 1
file.width = 640
}
}
}
# market titles from the MM relation - feeds the market facet
fields.market_stringM = SOLR_RELATION
fields.market_stringM {
localField = markets
multiValue = 1
}
type = tx_vitec_domain_model_usecase
# no_index is the editorial opt-out per story (concept 2026-08-20)
additionalWhereClause = hideonwebsite = 0 AND no_index = 0
fields {
title = title
content = SOLR_CONTENT
content {
cObject = COA
cObject {
10 = TEXT
10.field = subtitle
10.noTrimWrap = || |
20 = TEXT
20.field = teaser
20.noTrimWrap = || |
# the story body: inline tt_content children (text, columns,
# quotations) - same per-element extraction as page content
30 = CONTENT
30 {
table = tt_content
select {
pidInList.field = pid
where.dataWrap = {#tx_vitec_usecase_content} = {field:uid}
orderBy = sorting
}
renderObj < plugin.tx_solr.index.queue.pages.fields.content.cObject.10.renderObj
}
}
}
url = TEXT
url {
typolink {
# page 26 carries the vitec_usecaseshow plugin; the route
# enhancer turns this into /success-stories/story/<slug>
parameter = 26
additionalParams = &tx_vitec_usecaseshow[usecase]={field:uid}
additionalParams.insertData = 1
returnLast = url
}
}
}
}
# Solutions became indexable on 2026-09-11: the sitemap alignment gave all
# 39 records a detail_page (and slug). Same pattern as markets.
solutions = 1
solutions {
# result tile image: first reference, processed to 640px width
fields.image = FILES
fields.image {
references {
table = tx_vitec_domain_model_solution
uid.data = field:uid
fieldName = image
}
maxItems = 1
renderObj = IMG_RESOURCE
renderObj {
file.import.data = file:current:uid
file.treatIdAsReference = 1
file.width = 640
}
stdWrap.ifEmpty.cObject < lib.vitecIndexDetailImage
}
type = tx_vitec_domain_model_solution
additionalWhereClause = detail_page > 0
fields {
title = title
content = SOLR_CONTENT
content {
cObject = COA
cObject {
10 = TEXT
10.field = subtitle
10.noTrimWrap = || |
20 = TEXT
20.field = teaser
20.noTrimWrap = || |
30 = TEXT
30.field = description
30.noTrimWrap = || |
40 < lib.vitecIndexDetailText
}
}
url = TEXT
url {
typolink {
parameter.field = detail_page
returnLast = url
}
}
}
}
news = 1
news {
# crdate is the IMPORT date (Jul/Aug 2026) - the publication date
# lives in datetime. `created` feeds the news recency boost.
fields.created = datetime
# result tile image: first reference, processed to 640px width
fields.image = FILES
fields.image {
references {
table = tx_news_domain_model_news
uid.data = field:uid
fieldName = fal_media
}
maxItems = 1
renderObj = IMG_RESOURCE
renderObj {
file.import.data = file:current:uid
file.treatIdAsReference = 1
file.width = 640
}
}
type = tx_news_domain_model_news
# type 1 ("page as news") points at pages that are already indexed as
# pages - indexing them again would duplicate every one of them
additionalWhereClause = pid = 11 AND type = '0'
fields {
title = title
content = SOLR_CONTENT
content {
cObject = COA
cObject {
10 = TEXT
10.field = teaser
10.noTrimWrap = || |
20 = TEXT
20.field = bodytext
20.noTrimWrap = || |
}
}
url = TEXT
url {
typolink {
parameter = 21
additionalParams = &tx_news_pi1[controller]=News&tx_news_pi1[action]=detail&tx_news_pi1[news]={field:uid}
additionalParams.insertData = 1
returnLast = url
}
}
}
}
}
# Products are what visitors of a product site are usually looking for -
# without this boost, decades of news articles dominate the first result page
# for product-ish terms like "encoder". Stories get a smaller nudge.
plugin.tx_solr.search.query.boostQuery (
(type:tx_vitec_domain_model_product)^10.0 (type:tx_vitec_domain_model_usecase)^2.0
)
# Downloads have no detail page but a public file endpoint served by
# DownloadFileMiddleware. The where-clause keeps private / website-hidden
# downloads out of the public index (the middleware itself does not enforce
# private_download - the index must not advertise those files).
plugin.tx_solr.index.queue {
downloads = 1
downloads {
type = tx_vitec_domain_model_download
additionalWhereClause = private_download = 0 AND hideonwebsite = 0
fields {
title = title
content = SOLR_CONTENT
content {
cObject = COA
cObject {
10 = TEXT
10.field = teaser
10.noTrimWrap = || |
20 = TEXT
20.field = keywords
20.noTrimWrap = || |
30 = TEXT
30.field = description
30.noTrimWrap = || |
}
}
url = TEXT
url.dataWrap = /download/{field:slug}
}
}
}
# Type facet for the search JSON: counts per document type, serialized by
# SearchJsonRenderer as content.search.facets.type. keepAll* keeps the full
# counts visible while one type is selected - otherwise the tabs would
# collapse to the active one.
plugin.tx_solr.search.faceting = 1
plugin.tx_solr.search.faceting {
keepAllFacetsOnSelection = 1
facets {
type {
label = Type
field = type
keepAllOptionsOnSelection = 1
}
market {
label = Market
field = market_stringM
keepAllOptionsOnSelection = 1
}
category {
label = Category
field = category_stringM
keepAllOptionsOnSelection = 1
}
}
}
# =============================================================================
# Autocomplete endpoint: GET /search?type=7384&tx_solr[queryString]=<input>
# Lean JSON page type (no page envelope) for type-ahead in the React search
# field. Response: { suggestions: {term: count}, suggestion, documents[],
# didSecondSearch }.
# 10 is deliberately USER_INT, not USER as in the shipped example: the example
# predates TYPO3 v14 (config.no_cache is gone), and a cached USER would pin
# the first query's suggestions for every later request.
# =============================================================================
tx_solr_suggest = PAGE
tx_solr_suggest {
typeNum = 7384
config {
disableAllHeaderCode = 1
additionalHeaders.10.header = Content-Type: application/json; charset=utf-8
}
10 = USER_INT
10 {
userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
extensionName = Solr
pluginName = pi_suggest
vendorName = ApacheSolrForTypo3
controller = Suggest
action = suggest
}
}
plugin.tx_solr.suggest = 1
plugin.tx_solr.suggest {
numberOfSuggestions = 8
suggestField = spell
# term suggestions only: the built-in top-results carry the raw document
# type and the full indexed content - too fat and off-contract for type-ahead
showTopResults = 0
}
# News recency: old news decays against everything else. Additive edismax
# boost (bf): non-news documents get the constant 10, news get
# recip(age) = 10 when new, ~5 after one year, ~0.7 after 13 years.
# The gate keeps products/pages/stories out of the decay entirely.
plugin.tx_solr.search.query.boostFunction = if(termfreq(type,'tx_news_domain_model_news'),recip(ms(NOW,created),3.16e-11,10,1),10)