diff --git a/packages/vitec/Classes/EventListener/TikaDownloadContentIndexer.php b/packages/vitec/Classes/EventListener/TikaDownloadContentIndexer.php new file mode 100644 index 0000000..ff9c65b --- /dev/null +++ b/packages/vitec/Classes/EventListener/TikaDownloadContentIndexer.php @@ -0,0 +1,127 @@ +getIndexQueueItem(); + if ($item->getType() !== 'tx_vitec_domain_model_download') { + return; + } + + $file = DownloadFileResolver::resolve($item->getRecordUid()); + if ( + $file === null + || $file['size'] <= 0 + || $file['size'] > self::MAX_FILE_SIZE + || !in_array($file['mimeType'], self::EXTRACTABLE_MIME_TYPES, true) + ) { + return; + } + + $text = $this->extract($file['path']); + if ($text === '') { + return; + } + + $document = $event->getDocument(); + $existing = trim((string)($document['content'] ?? '')); + $document->setField('content', trim($existing . ' ' . $text)); + } catch (\Throwable) { + // fail soft - the download stays indexed with its metadata + } + } + + private function extract(string $path): string + { + $cacheFile = $this->cacheFileFor($path); + if ($cacheFile !== '' && is_file($cacheFile)) { + return (string)file_get_contents($cacheFile); + } + + $baseUrl = (string)getenv('TIKA_URL'); + if ($baseUrl === '') { + return ''; + } + + $context = stream_context_create(['http' => [ + 'method' => 'PUT', + 'header' => [ + 'Authorization: Basic ' . base64_encode(getenv('TIKA_USERNAME') . ':' . getenv('TIKA_PASSWORD')), + 'Accept: text/plain', + 'Content-Type: application/octet-stream', + ], + 'content' => (string)file_get_contents($path), + 'timeout' => 30, + 'ignore_errors' => true, + ]]); + $raw = file_get_contents(rtrim($baseUrl, '/') . '/tika', false, $context); + $status = (string)($http_response_header[0] ?? ''); + if ($raw === false || !str_contains($status, '200')) { + return ''; + } + + $text = mb_substr(trim((string)preg_replace('/\s+/u', ' ', $raw)), 0, self::MAX_TEXT_LENGTH); + + if ($cacheFile !== '' && $text !== '') { + GeneralUtility::mkdir_deep(dirname($cacheFile)); + GeneralUtility::writeFile($cacheFile, $text); + } + + return $text; + } + + /** + * Cache key covers path, size and mtime - replacing a file re-extracts. + */ + private function cacheFileFor(string $path): string + { + $size = @filesize($path); + $mtime = @filemtime($path); + if ($size === false || $mtime === false) { + return ''; + } + return Environment::getVarPath() . '/tika-cache/' + . sha1($path . '|' . $size . '|' . $mtime) . '.txt'; + } +} diff --git a/packages/vitec/Classes/UserFunc/ProductFinderJsonRenderer.php b/packages/vitec/Classes/UserFunc/ProductFinderJsonRenderer.php new file mode 100644 index 0000000..db3d54f --- /dev/null +++ b/packages/vitec/Classes/UserFunc/ProductFinderJsonRenderer.php @@ -0,0 +1,150 @@ +cObj stays null and the element cannot read its own FlexForm. + */ +final class ProductFinderJsonRenderer +{ + private const CATEGORY_TABLE = 'sys_category'; + + /** Fallback when no root is configured: the category tree the products use. */ + private const DEFAULT_ROOT_TITLE = 'Product'; + + protected ?ContentObjectRenderer $cObj = null; + + public function setContentObjectRenderer(ContentObjectRenderer $cObj): void + { + $this->cObj = $cObj; + } + + #[AsAllowedCallable] + public function render(string $content, array $conf): string + { + try { + $settings = $this->settings(); + + $root = (int)($settings['categoryRoot'] ?? 0); + if ($root <= 0) { + $root = $this->rootByTitle(self::DEFAULT_ROOT_TITLE); + } + if ($root <= 0) { + return (string)json_encode(['categories' => []]); + } + + $order = (string)($settings['sorting'] ?? 'sorting'); + $categories = []; + foreach ($this->childrenOf($root, $order) as $main) { + $categories[] = [ + 'uid' => (int)$main['uid'], + 'title' => (string)$main['title'], + 'subcategories' => array_map( + static fn(array $sub): array => [ + 'uid' => (int)$sub['uid'], + 'title' => (string)$sub['title'], + ], + $this->childrenOf((int)$main['uid'], $order) + ), + ]; + } + + return (string)json_encode(['categories' => $categories], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } catch (\Throwable) { + // A broken filter must never take the page payload down with it. + return (string)json_encode(['categories' => []]); + } + } + + /** + * FlexForm settings of the element being rendered. + * + * @return array + */ + private function settings(): array + { + $flexform = (string)($this->cObj->data['pi_flexform'] ?? ''); + if ($flexform === '') { + return []; + } + $parsed = GeneralUtility::makeInstance(FlexFormService::class) + ->convertFlexFormContentToArray($flexform); + + return is_array($parsed['settings'] ?? null) ? $parsed['settings'] : []; + } + + /** + * Direct children of a category, honouring the frontend restrictions + * (hidden, start/end time, deleted). + * + * @return array> + */ + private function childrenOf(int $parent, string $order): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable(self::CATEGORY_TABLE); + $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class)); + + $query = $queryBuilder->select('uid', 'title', 'sorting') + ->from(self::CATEGORY_TABLE) + ->where( + $queryBuilder->expr()->eq('parent', $queryBuilder->createNamedParameter($parent, ParameterType::INTEGER)), + $queryBuilder->expr()->in('sys_language_uid', [-1, 0]) + ); + + match ($order) { + 'title' => $query->orderBy('title', 'ASC'), + 'uid' => $query->orderBy('uid', 'ASC'), + default => $query->orderBy('sorting', 'ASC')->addOrderBy('title', 'ASC'), + }; + + return $query->executeQuery()->fetchAllAssociative(); + } + + /** Uid of a root-level category by title, 0 when it does not exist. */ + private function rootByTitle(string $title): int + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable(self::CATEGORY_TABLE); + $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class)); + + $uid = $queryBuilder->select('uid')->from(self::CATEGORY_TABLE) + ->where( + $queryBuilder->expr()->eq('parent', 0), + $queryBuilder->expr()->eq('title', $queryBuilder->createNamedParameter($title)) + ) + ->setMaxResults(1) + ->executeQuery()->fetchOne(); + + return is_numeric($uid) ? (int)$uid : 0; + } +} diff --git a/packages/vitec/Classes/UserFunc/SearchJsonRenderer.php b/packages/vitec/Classes/UserFunc/SearchJsonRenderer.php index aa92742..27eaa13 100644 --- a/packages/vitec/Classes/UserFunc/SearchJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/SearchJsonRenderer.php @@ -73,6 +73,7 @@ class SearchJsonRenderer 'pages' => 'page', 'tx_vitec_domain_model_product' => 'product', 'tx_vitec_domain_model_market' => 'market', + 'tx_vitec_domain_model_solution' => 'solution', 'tx_vitec_domain_model_usecase' => 'story', 'tx_news_domain_model_news' => 'news', 'tx_vitec_domain_model_download' => 'download', @@ -152,6 +153,15 @@ class SearchJsonRenderer $activeType = ''; } + // Secondary facet filters: value comes verbatim from facets.[].value + $facetFilters = []; + foreach (['market', 'category'] as $facetParam) { + $facetValue = trim((string)($params[$facetParam] ?? '')); + if ($facetValue !== '') { + $facetFilters[$facetParam] = $facetValue; + } + } + if ($query === '') { return (string)json_encode($this->emptyResult()); } @@ -177,8 +187,15 @@ class SearchJsonRenderer $search, ); $arguments = ['q' => $query, 'page' => $page]; + $filterArguments = []; if ($activeType !== '') { - $arguments['filter'] = ['type:' . $typeFilterField]; + $filterArguments[] = 'type:' . $typeFilterField; + } + foreach ($facetFilters as $facetName => $facetValue) { + $filterArguments[] = $facetName . ':' . $facetValue; + } + if ($filterArguments !== []) { + $arguments['filter'] = $filterArguments; } $searchRequest = GeneralUtility::makeInstance(SearchRequestBuilder::class, $typoScriptConfiguration) ->buildForSearch($arguments, $pageId, $languageId); @@ -255,6 +272,7 @@ class SearchJsonRenderer 'numFound' => $numFound, 'totalPages' => $resultsPerPage > 0 ? (int)ceil($numFound / $resultsPerPage) : 0, 'filter' => $activeType !== '' ? $activeType : null, + 'activeFacets' => (object)$facetFilters, 'facets' => (object)$facets, 'results' => $results, 'suggestions' => array_values(array_unique($suggestions)), @@ -276,6 +294,7 @@ class SearchJsonRenderer 'numFound' => 0, 'totalPages' => 0, 'filter' => null, + 'activeFacets' => new stdClass(), 'facets' => new \stdClass(), 'results' => [], 'suggestions' => [], diff --git a/packages/vitec/Configuration/FlexForms/Productfinder.xml b/packages/vitec/Configuration/FlexForms/Productfinder.xml new file mode 100644 index 0000000..bd32c31 --- /dev/null +++ b/packages/vitec/Configuration/FlexForms/Productfinder.xml @@ -0,0 +1,58 @@ + + + + + + + Product Finder + array + + + + The category whose children become the filter's main categories. Leave empty to use the product category tree ("Product"). + + select + selectSingle + sys_category + AND sys_category.parent = 0 AND sys_category.sys_language_uid IN (-1,0) ORDER BY sys_category.title ASC + + + Product category tree (default) + 0 + + + 0 + + + + + + Applies to the main categories and their subcategories alike. + + select + selectSingle + + + Backend order (as arranged in the category tree) + sorting + + + Alphabetically by title + title + + + By uid + uid + + + sorting + + + + + + + diff --git a/packages/vitec/Configuration/Sets/Vitecset/setup.typoscript b/packages/vitec/Configuration/Sets/Vitecset/setup.typoscript index 5a1b0df..fa086c3 100755 --- a/packages/vitec/Configuration/Sets/Vitecset/setup.typoscript +++ b/packages/vitec/Configuration/Sets/Vitecset/setup.typoscript @@ -67,6 +67,21 @@ tt_content { } } + # 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 { @@ -389,7 +404,14 @@ plugin.tx_solr.index.queue { products = 1 products { + # 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 @@ -466,7 +488,15 @@ plugin.tx_solr.index.queue { usecases = 1 usecases { + # 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 @@ -495,6 +525,39 @@ plugin.tx_solr.index.queue { } } +# 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 { + 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 = || | + } + } + url = TEXT + url { + typolink { + parameter.field = detail_page + returnLast = url + } + } + } + } + news = 1 news { type = tx_news_domain_model_news @@ -580,6 +643,16 @@ plugin.tx_solr.search.faceting { field = type keepAllOptionsOnSelection = 1 } + market { + label = Market + field = market_stringM + keepAllOptionsOnSelection = 1 + } + category { + label = Category + field = category_stringM + keepAllOptionsOnSelection = 1 + } } } diff --git a/packages/vitec/Configuration/TCA/Overrides/tt_content.php b/packages/vitec/Configuration/TCA/Overrides/tt_content.php index d79b06d..9333e7b 100644 --- a/packages/vitec/Configuration/TCA/Overrides/tt_content.php +++ b/packages/vitec/Configuration/TCA/Overrides/tt_content.php @@ -25,6 +25,7 @@ use TYPO3\CMS\Extbase\Utility\ExtensionUtility; }; $registerPluginWithFlexForm('Productlist', 'Show Products by Category', 'FILE:EXT:vitec/Configuration/FlexForms/Productlist.xml', 'vitec-plugin-productlist'); + $registerPluginWithFlexForm('Productfinder', 'Product Finder (filter data)', 'FILE:EXT:vitec/Configuration/FlexForms/Productfinder.xml', 'vitec-plugin-productlist'); $registerPluginWithFlexForm('Simplecard', 'Simple Card', 'FILE:EXT:vitec/Configuration/FlexForms/Simplecard.xml', 'vitec-plugin-simplecard'); $registerPluginWithFlexForm('Productshow', 'Show Single Product', 'FILE:EXT:vitec/Configuration/FlexForms/Productshow.xml', 'vitec-plugin-productshow'); $registerPluginWithFlexForm('Usecaseshow', 'Single Success Story', 'FILE:EXT:vitec/Configuration/FlexForms/Usecase.xml', 'vitec-plugin-usecaseshow'); diff --git a/packages/vitec/Configuration/page.tsconfig b/packages/vitec/Configuration/page.tsconfig index 6548810..1894d47 100755 --- a/packages/vitec/Configuration/page.tsconfig +++ b/packages/vitec/Configuration/page.tsconfig @@ -16,6 +16,14 @@ mod { CType = vitec_productlist } } + productfinder { + iconIdentifier = vitec-plugin-productlist + title = Product Finder (filter data) + description = Delivers the product category hierarchy for the filter selects. Place once above the product lists - it renders no products itself. + tt_content_defValues { + CType = vitec_productfinder + } + } productshow { iconIdentifier = vitec-plugin-productshow title = Show single VITEC Product diff --git a/packages/vitec/Documentation/Headless-JSON-Architecture.md b/packages/vitec/Documentation/Headless-JSON-Architecture.md index e5ef0d0..6c2e2ae 100755 --- a/packages/vitec/Documentation/Headless-JSON-Architecture.md +++ b/packages/vitec/Documentation/Headless-JSON-Architecture.md @@ -3,9 +3,9 @@ | | | |---|---| | **Document identifier** | EVO‑VITEC‑HL‑001 | -| **Version** | 1.12 | +| **Version** | 1.15 | | **Status** | Released | -| **Date** | 2026‑08‑24 | +| **Date** | 2026‑09‑11 | | **Applies to** | `evomedien/vitec` on TYPO3 v14.3 (headless) | | **Owner** | evomedien — VITEC relaunch | @@ -26,6 +26,9 @@ | 1.10 | 2026‑08‑18 | **Interface change (additive).** `tx_vitec_domain_model_product` gained five fields: `heroimage` (multiple FAL images, detail payload only), the richtext fields `description2`, `capabilities` and `textrelatedproducts`, and `portfolio` (TCA `link`) — emitted as a **resolved URL** through the new `LinkResolver::typolinkUrl()`. The product payloads are specified for the first time (7.15). Links in `contentelement` / `contentelementcta` that point at a **container** now resolve its children (`items`, page‑level shape) and `background` (7.4). Two record link handlers (`download`, `product`) added to the link browser, resolved server‑side per the new Clause 9.12; new middleware `vitec/download-file` streams `/download/file/` as a forced download (5.3), file lookup consolidated into `DownloadFileResolver` — first step towards the B‑4 target (10.2). Backend‑only: `relatedprodukt` moved from the Misc tab to General. Editorial: the document footer had been stuck at v1.7 since v1.8. | | 1.11 | 2026‑08‑21 | **New interface: site search.** Apache Solr 10 (dedicated VPS behind an HTTPS reverse proxy) with `apache-solr-for-typo3/solr` 14.0.0-RC1. The EXT:solr results plugin `solr_pi_results` on the search page is rendered headless by `SearchJsonRenderer` (payload key `search`) - request/response contract in the new Clause 7.16. Indexed corpus: pages plus product, market (`detail_page` only), use-case, news and download records; result `type` vocabulary `page\|product\|market\|story\|news\|download`. Downloads gained the canonical route `/download/` (uid route kept for the record links) and `private_download` is now enforced by `DownloadFileResolver` (5.3, 9.12). The VITEC Set now declares the solr set as a dependency - overriding a foreign set’s TypoScript requires loading after it (5.2). Editorial: the header table had been stuck at v1.9 since v1.10. | | 1.12 | 2026‑08‑24 | **Interface change (additive).** The search endpoint (7.16) gained a type filter and facet counts: request parameter `filter` (a value from the `type` vocabulary; the natural name `type` is unavailable - it is TYPO3’s reserved page-type parameter), response keys `filter` (active filter or null) and `facets.type` (per-type document counts with `active` flags; counts stay complete while a filter is active, except when the filtered result is empty). Editorial baseline of 76 managed synonyms imported into `core_en` (codecs, acquired-brand names such as `exterity => avedia`, UK/US spellings, common misspellings) - synonyms apply at query time, no re-index. New autocomplete endpoint: the EXT:solr suggest plugin as lean JSON page type 7384, deliberately USER_INT (7.16). | +| 1.13 | 2026‑09‑11 | **New plugin** `vitec_productfinder` (`ProductFinderJsonRenderer`, payload key `productfinder`): the product category hierarchy on its own, as the data source for the filter selects above the product lists (7.15.3, 8). No change to `vitec_productlist` — filtering happens client side over the products already delivered, keyed on the category uids this payload carries. | +| 1.14 | 2026‑09‑11 | **Interface change (additive).** Secondary search facets: stories index their market titles (`market_stringM`, MM relation), products their sys_category titles (`category_stringM`); the search endpoint accepts `market` and `category` GET parameters (values verbatim from `facets.[].value`; an unknown value simply yields zero results) and echoes them as `activeFacets`. Visibility fix: the usecases queue now honours `no_index`/`hideonwebsite`, products defensively `hideonwebsite` - and the excluded story’s document was removed explicitly, because re-indexing never deletes. | +| 1.15 | 2026‑09‑11 | **Corpus change (additive, index side).** Download documents now carry the extracted file text: Solr 10 has no ExtractingRequestHandler, so a dedicated Apache Tika container on the Solr VPS (Caddy route `/tika/*`, own basic-auth credential) extracts PDF/Office contents during indexing (`TikaDownloadContentIndexer` on `BeforeDocumentIsProcessedForIndexingEvent`, fail-soft per 9.5, cached in `var/tika-cache` keyed on path+size+mtime). Datasheet specifications are now searchable ("625i", "genlock"). No interface change. | This document is drafted in the style of, and adopts the terminology conventions of, ISO/IEC/IEEE 42010 (architecture description), ISO/IEC/IEEE 26514 (information for @@ -904,6 +907,46 @@ rarely shows. The `layout` vocabulary remains `0`–`3` (see the v1.9 note). --- +#### 7.15.3 Product Finder payload (`vitec_productfinder`, key `productfinder`) + +The filter above the product lists is fed by its own element. It carries the +product **category hierarchy only** — no products, no counts: + +```json +{ + "type": "vitec_productfinder", + "content": { + "productfinder": { + "categories": [ + { + "uid": 58, + "title": "Platforms and End-Points", + "subcategories": [ + { "uid": 66, "title": "Avedia Platform" }, + { "uid": 67, "title": "EZ TV Platform" }, + { "uid": 68, "title": "APEX Platform" } + ] + } + ] + } + } +} +``` + +`categories[]` are the children of the configured root category, `subcategories[]` +their direct children — the same two levels the product records hang on, so a +selected value maps straight onto `product.categories[].uid` in the list payload +(7.15.2). The editor places **one** element above the lists; FlexForm offers the +category root (empty = the `Product` tree) and the order of both levels (backend +order, title, uid). + +Filtering itself is a front-end concern: the list plugin is unchanged, and the +front end narrows the products it already received using the URL parameters +`?cat=` and `?subcat=`. Categories without products are emitted like any other — +the front end decides whether to offer or suppress an empty result. + +--- + ### 7.16 Search payload (`solr_pi_results`) The site search runs on Apache Solr through EXT:solr. The EXT:solr results plugin @@ -919,6 +962,8 @@ only the rendering differs from the stock Fluid plugin. | `q` | Search terms. Absent or empty: the response keeps its full shape with `numFound: 0`, so the front end never needs a second code path. | | `page` | 1-based result page, optional. | | `filter` | Restrict results to one type from the `type` vocabulary below (e.g. `filter=product`). Unknown values are ignored. Named `filter` because `type` is TYPO3’s reserved page-type parameter. | +| `market` | Restrict to one market (stories carry the facet). Value verbatim from `facets.market[].value`; unknown values yield zero results. | +| `category` | Restrict to one product category. Value verbatim from `facets.category[].value`. | The EXT:solr namespace (`tx_solr[q]`, `tx_solr[page]`) is accepted as a fallback. All three parameters are excluded from cHash validation; the search page is never @@ -936,7 +981,11 @@ identifier). "numFound": 202, "totalPages": 21, "filter": null, + "activeFacets": {}, "facets": { + "market": [ + { "value": "Sports, Venues & Entertainment", "count": 9, "active": false } + ], "type": [ { "value": "news", "count": 137, "active": false }, { "value": "product", "count": 13, "active": false } @@ -963,14 +1012,19 @@ identifier). tabs do not collapse - except when the filtered result is empty, where only the active option (count 0) is returned; front ends should then offer "remove filter" rather than rely on the other counts. -- `filter` echoes the active type filter, `null` when none. +- `filter` echoes the active type filter, `null` when none; `activeFacets` + echoes the active secondary facet filters as `{name: value}`, `{}` when none. +- `facets.market` (story market titles, from the MM relation) and + `facets.category` (product sys_category titles) appear whenever matching + documents carry the fields; further facets are pure TypoScript. - `suggestions` lists spellcheck alternatives ("did you mean"), `[]` if none. **Indexed corpus** (`plugin.tx_solr.index.queue`): pages, plus records with a resolvable public URL - products, markets (only those with `detail_page`), use cases, news (`type = 0`; "page as news" records are excluded because their target pages are already indexed) and downloads (`private_download = 0 AND -hideonwebsite = 0`). Solutions are not indexed until they carry slugs or detail +hideonwebsite = 0`; their `content` additionally carries the file text extracted +through Apache Tika, so datasheet specifications are searchable). Solutions are not indexed until they carry slugs or detail pages. Ranking boosts products (^10) and stories (^2). Every executed search is logged to `tx_solr_statistics` with the last two IP octets masked. @@ -1005,6 +1059,7 @@ content is extracted from the page’s `tt_content` rows via | CType | TS pattern | Renderer / Processor | Payload key | Kind | |---|---|---|---|---| | `vitec_productlist` | `< lib.contentElementWithHeader` | ProductListJsonRenderer | `products` | list | +| `vitec_productfinder` | `< lib.contentElementWithHeader` | ProductFinderJsonRenderer | `productfinder` | taxonomy | | `vitec_productshow` | `< lib.…WithHeader` | ProductShowJsonRenderer | `product` | detail | | `vitec_usecaselist` | `< lib.…WithHeader` | UsecaseListJsonRenderer → **UsecaseSerializer** | `usecases` | list | | `vitec_usecaseshow` | `< lib.…WithHeader` | UsecaseShowJsonRenderer → **UsecaseSerializer** | `usecase` | detail | @@ -1368,4 +1423,4 @@ remediation. - Header convention — the uniform header section across CEs, plugins and containers. - `Configuration/Sets/Vitecset/setup.typoscript` — the single TypoScript entry point. -*End of document EVO‑VITEC‑HL‑001 v1.12.* +*End of document EVO‑VITEC‑HL‑001 v1.15.* diff --git a/packages/vitec/ext_localconf.php b/packages/vitec/ext_localconf.php index a4014d9..97443c0 100755 --- a/packages/vitec/ext_localconf.php +++ b/packages/vitec/ext_localconf.php @@ -258,3 +258,5 @@ $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1750000000] = [ $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'q'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'page'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'filter'; +$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'market'; +$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'category';