Files
frommeandClaude Opus 5.5 1d7fadbd19 feat: favicons as a realfavicongenerator set per site, like ep-reisen and snowbreak
The theme registered 26 icons (16 to 196 px PNGs, a GIF, eleven Apple touch
icons) that current browsers no longer need. It now renders the five tags of
the set realfavicongenerator.net generates (96 px PNG, SVG, ICO, 180 px
Apple touch icon, web manifest) plus apple-mobile-web-app-title, from two
constants: faviconPath, the folder in fileadmin the set was uploaded to
(default: the E&P set), and faviconAppTitle.

The ICO also matches the favicon parser of yoast's snippet preview now
(rel="shortcut icon" directly followed by href), which missed the old tags
and fell back to a /favicon.ico none of the sites has.

cllt and alvac, which showed the theme's E&P icon, get the E&P set. Sites
whose own template sets page.shortcutIcon and removes headerData.333
(ski-boarderweek, snowzone, ser) keep their icon until they have a set; the
remaining ICOs of these three stay until then. ep-reisen and snowbreak still
override the tags with the same markup in their templates.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 15:19:05 +01:00

212 lines
8.2 KiB
Plaintext

page = PAGE
page {
meta {
viewport = width=device-width, initial-scale=1
X-UA-Compatible = ie=edge
format-detection = telephone=no
google-site-verification = {$plugin.tx_eptheme.settings.googleSiteVerification}
}
bodyTagCObject = COA
bodyTagCObject {
10 = TEXT
10.value = <body class="antialiased text-zinc-800 font-sans pt-14 lg:pt-8 bg-white
20 = TEXT
20 {
field = tx_eptheme_bodyclass
if.isTrue.field = tx_eptheme_bodyclass
noTrimWrap = | ||
}
30 = TEXT
30 {
value = {$plugin.tx_eptheme.settings.themekey}
if.isTrue = {$plugin.tx_eptheme.settings.themekey}
noTrimWrap = | ||
}
35 = TEXT
35 {
field = uid
noTrimWrap = | page-||
}
40 = TEXT
40.value = ">
}
# Every image on the page comes from the CDN host (see Extensions/Replacer.typoscript),
# so a cold visitor pays a full DNS + TCP + TLS handshake - measured at 50-70ms - before
# the first one can start downloading. `preconnect` gets that out of the way while the
# HTML is still parsing. No `crossorigin`: images are fetched without CORS, and an
# anonymous-vs-CORS mismatch would open a second connection instead of reusing this one.
# Fonts, which would need it, are served by this host now.
headerData.100 = TEXT
headerData.100.value = <link rel="preconnect" href="https://cdn.ep-reisen.de">
# The set realfavicongenerator.net generates (2024): what current browsers use, nothing for
# outdated ones. faviconPath is the folder in fileadmin it was uploaded to.
headerData.333 = TEXT
headerData.333.value (
<link rel="icon" type="image/png" href="/{$plugin.tx_eptheme.settings.faviconPath}/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/{$plugin.tx_eptheme.settings.faviconPath}/favicon.svg" />
<link rel="shortcut icon" href="/{$plugin.tx_eptheme.settings.faviconPath}/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/{$plugin.tx_eptheme.settings.faviconPath}/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-title" content="{$plugin.tx_eptheme.settings.faviconAppTitle}" />
<link rel="manifest" href="/{$plugin.tx_eptheme.settings.faviconPath}/site.webmanifest" />
)
# disableCompression keeps ResourceCompressor's hands off the Encore entries: Webpack
# already minifies them, and copying them to typo3temp/assets/compressed only replaces the
# content hash with a name derived from md5(abspath + mtime + size), which changes on every
# deploy even when the bytes do not - defeating the immutable caching the hash exists for.
# RequestHandler turns this into `compress => false`, which PageRendererHooks forwards to
# typo3_encore's TagRenderer (whose own default it would otherwise override).
includeCSS {
main = PKG:ssch/typo3-encore:EpThemeConfig:main
# Skips TYPO3's file path check, which would drop the entry name before typo3_encore's hook sees it
main.external = 1
main.disableCompression = 1
}
includeJSFooter {
main = PKG:ssch/typo3-encore:EpThemeConfig:main
# Skips TYPO3's file path check, which would drop the entry name before typo3_encore's hook sees it
main.external = 1
main.defer = 1
main.disableCompression = 1
}
10 = FLUIDTEMPLATE
10 {
file {
cObject = TEXT
cObject {
data = levelfield: -1, backend_layout_next_level, slide
override {
data = page:backend_layout
}
ifEmpty = Default
split {
token = pagets__
1.current = 1
1.wrap = |
}
wrap = EXT:ep_theme/Resources/Private/Templates/Page/|.html
case = ucfirst
}
}
layoutRootPaths.0 = EXT:ep_theme/Resources/Private/Layouts/
partialRootPaths.0 = EXT:ep_theme/Resources/Private/Partials/
templateRootPaths.0 = EXT:ep_theme/Resources/Private/Templates/
dataProcessing {
10 = EP\EpTheme\DataProcessing\ContextProcessor
20 = EP\EpTheme\DataProcessing\SliderProcessor
25 = EP\EpTheme\DataProcessing\ProductRatingProcessor
30 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
30 {
table = tx_epproducts_domain_model_badge
where.data = field:tx_eptheme_badge
where.wrap = uid=|
pidInList = {$plugin.tx_eptheme.settings.badgeStoragePid}
as = badge
if {
equals.field = backend_layout
value = pagets__detail
negate = 1
}
}
40 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
40 {
special = directory
special.value = {$plugin.tx_eptheme.settings.topNavPid}
levels = 1
as = topnav
}
50 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
50 {
special = directory
special.value = {$plugin.tx_eptheme.settings.mainNavPid}
levels = 3
titleField = title
as = mainnav
}
60 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
60 {
special = directory
special.value = {$plugin.tx_eptheme.settings.mobileNavPid}
levels = 4
expandAll = 1
titleField = title
as = mobilenav
}
70 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor
70 {
special = directory
special.value = {$plugin.tx_eptheme.settings.footerNavPid}
levels = 2
expandAll = 1
titleField = title
as = footernav
}
80 = TYPO3\CMS\Frontend\DataProcessing\SiteProcessor
80 {
as = site
}
}
settings < plugin.tx_eptheme.settings
}
headerData.990 = TEXT
headerData.990 {
value = <script src="{$plugin.tx_eptheme.settings.cookieFirstScriptUrl}"></script>
if.isTrue = {$plugin.tx_eptheme.settings.cookieFirstScriptUrl}
}
headerData.995 = TEXT
headerData.995 {
value (
<!-- Google Tag Manager -->
<script type="text/plain" data-cookiefirst-category="necessary">(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','{$plugin.tx_eptheme.settings.googleTagManagerId}');</script>
<!-- End Google Tag Manager -->
)
if.isTrue = {$plugin.tx_eptheme.settings.googleTagManagerId}
}
}
[traverse(page, "backend_layout") == 'pagets__popup']
page.bodyTagCObject >
page.bodyTagCObject = TEXT
page.bodyTagCObject.value = <body class="antialiased text-zinc-800 font-sans bg-white p-4">
page.headerData.333 >
# page.headerData.990 >
# page.headerData.995 >
[end]
[traverse(page, "backend_layout") == 'pagets__travelinfo']
page.bodyTagCObject >
page.bodyTagCObject = TEXT
page.bodyTagCObject.value = <body class="antialiased text-zinc-800 font-sans bg-white">
[end]
[request.getNormalizedParams() && like(request.getNormalizedParams().getHttpUserAgent(), "*Googlebot*")]
page.headerData.990 >
page.headerData.995 >
[end]
# Comparisons with TYPO3 10 (.ddev/plans/checks): an element that fails renders the error placeholder
# instead of failing the page, so the other elements of the page can still be compared
[applicationContext == "Development" && traverse(request.getHeaders(), 'x-ep-compare/0') == '1']
config.contentObjectExceptionHandler = 1
[end]
#[applicationContext == "Development" || applicationContext == "Production/Staging"]
# page.headerData.990 >
# page.headerData.995 >
#[end]