Einbindung von App Icons in TYPO3
→ zur englischen Version
Ich möchte Euch heute mal meinen Weg zeigen, wie ich App Icons per TypoScript sauber in das eigene TYPO3 Sitepackage einbinde.
App Icon Generator
Da App Icons, je nach Hersteller und Browser, auf unterschiedliche Art in HTML eingebunden werden müssen und entsprechend viele Grafik- und Meta-Dateien im Spiel sind, empfiehlt es sich, dafür einen Generator zu benutzen. Dieser nimmt Euch die Arbeit ab, alle relevanten und zeitgemäßen Dateien und HTML-Zeilen selbst bei den jeweiligen Herstellen (Apple, Microsoft, Google, etc.) zu recherchieren. Ich kann Euch beispielsweise diesen Generator hier empfehlen: https://realfavicongenerator.net/
Nachdem Ihr Euch durch die GUI des Generators geklickt habt, erhaltet Ihr diese (oder ähnliche) Dateien:
.
├── README.md
├── android-chrome-144x144.png
├── android-chrome-192x192.png
├── android-chrome-256x256.png
├── android-chrome-36x36.png
├── android-chrome-384x384.png
├── android-chrome-48x48.png
├── android-chrome-512x512.png
├── android-chrome-72x72.png
├── android-chrome-96x96.png
├── apple-touch-icon.png
├── browserconfig.xml
├── favicon-16x16.png
├── favicon-194x194.png
├── favicon-32x32.png
├── favicon.ico
├── html_code.html
├── mstile-144x144.png
├── mstile-150x150.png
├── mstile-310x150.png
├── mstile-310x310.png
├── mstile-70x70.png
├── safari-pinned-tab.svg
└── site.webmanifest
Die Installationsanweisungen aus der README.md
sind recht einfach gehalten. Hier wird dazu geraten alle Dateien im Projekt-Root abzulegen. Das kriegen wir im TYPO3-Kontext aber eleganter hin 😉
TYPO3 Integration der App Icons
Da ich den Projekt-Root gerne sauber halte, würde ich dazu raten, die Dateien in den üblichen TYPO3-Strukturen Eures Sitepackages abzulegen, z.B. unter EXT:sitepackage/Resources/Public/Icons/Favicon/
.
Folgende Dateien braucht Ihr nicht in dieses Verzeichnis zu kopieren:
README.md
→ Diese enthält die Installationsanweisungen
html_code.html
→ Diese enthält den zu integrierenden HTML-Code
browserconfig.xml
→ Diese wird später dynamisch per TypoScript erzeugt
site.webmanifest
→ Diese wird später dynamisch per TypoScript erzeugt
Einzubettender HTML Code im <head>
Tag
Laut README.md
/html_code.html
soll der folgende Code eingebunden werden:
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="194x194" href="/favicon-194x194.png">
<link rel="icon" type="image/png" sizes="192x192" href="/android-chrome-192x192.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-config" content="/browserconfig.xml">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
<meta name="theme-color" content="#ffffff">
Randnotiz:
Die Zeile <meta name="msapplication-config" content="/browserconfig.xml">
habe ich ergänzt, um den IE sicherheitshalber explizit anzuweisen die browserconfig.xml
aus dem Root zu verwenden. Hierzu gibt es diverse Diskussionen und ich denke mir sicher ist sicher 😉.
Dies wird mit folgendem TypoScript Setup Code eingebunden:
page {
headerData {
10 = TEXT
10 {
value (
<link rel="apple-touch-icon" sizes="180x180" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/apple-touch-icon.png}">
<link rel="icon" type="image/png" sizes="32x32" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/favicon-32x32.png}">
<link rel="icon" type="image/png" sizes="194x194" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/favicon-194x194.png}">
<link rel="icon" type="image/png" sizes="192x192" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/android-chrome-192x192.png}">
<link rel="icon" type="image/png" sizes="16x16" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/favicon-16x16.png}">
<link rel="manifest" href="/site.webmanifest">
<link rel="mask-icon" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/safari-pinned-tab.svg}" color="#5bbad5">
<meta name="msapplication-config" content="/browserconfig.xml">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-TileImage" content="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/mstile-144x144.png}">
<meta name="theme-color" content="#ffffff">
)
insertData = 1
}
}
}
Angenommen wurde hier, dass Ihr zuvor bereits irgendwo page = PAGE
definiert habt, um das Standard Frontend-Rendering Eurer Seite unter dem theoretisch frei wählbaren Bezeichner page
zu definieren. Die Verwendung von page
für die Standardausgabe ist etablierte Praxis. Außerdem vorausgesetzt wird, dass Ihr unter page.headerData.10
nicht bereits andere Angaben gemacht habt. Falls doch, könnt Ihr einfach den Content Object Array Index 10
(empfohlenerweise in Zehnerschritten) bis zu einem freien Platz erhöhen.
Die Angaben {path:EXT:sitepackage/Resources/Public/Icons/Favicon/<DATEINAME>}
innerhalb des TEXT
-Wertes in Kombination mit der Angabe insertData = 1
sorgt dafür, dass die Dateien in Composer-Installationen dynamisch unter /_assets/<HASH>/
referenziert werden. Hier haben wir ja die Situation, dass es keinen statischen Pfad mehr gibt, unter dem Ressourcen des Extension-Verzeichnisses /Resources/Public/
aufrufbar sind (mehr dazu hier).
favicon.ico
für ältere Browser
Die klassische favicon.ico
-Datei für Desktop-Browser wird generiert, aber nicht mehr explizit im Einbettungscode bereitgestellt. Auch wenn es in den meisten Fällen nicht mehr notwendig ist, würde ich diese Datei dennoch einbinden, nicht zuletzt weil TYPO3 eine eigene TypoScript-Eigenschaft dafür bereitstellt.
page.shortcutIcon = EXT:sitepackage/Resources/Public/Icons/Favicon/favicon.ico
Im Falle dieser Datei könntet Ihr aber auch eine Ausnahme machen und darüber nachdenken, diese direkt im öffentlichen Root abzulegen. Es scheint manche Tools zu geben (z.B. RSS Reader), die nicht mit anderen Pfaden umgehen können.
Angaben in der Site Configuration
Nun müssen wir uns nurnoch darum kümmern, dass auch site.webmanifest
und browserconfig.xml
im Root aufrufbar sind, obwohl diese dort nicht tatsächlich als Dateien existieren.
Dies erreichen wir über zwei Ergänzungen im PageTypeSuffix
Route Enhancer Mapping in unserer Site Configuration config/sites/<DEIN_IDENTIFIKATOR>/config.yaml
:
browserconfig.xml: 2943879438
site.webmanifest: 3478304621
Die Page Type Nummern 2943879438
und 3478304621
werden also auf die lesbaren Pfade browserconfig.xml
und site.webmanifest
gemappt. Die beiden Nummern sind von mir frei gewählt. Wichtig ist nur, dass diese nirgendwo sonst verwendet werden. Der Pfad sitemap.xml
der SEO Core Extension beansprucht beispielsweise bereits die Type Nummer 1533906435
.
Der gesamte PageTypeSuffix
Konfigurations-Block könnte (je nach Projekt) am Ende so aussehen:
routeEnhancers:
PageTypeSuffix:
type: PageType
default: /
index: ''
map:
/: 0
feed.rss: 9818
sitemap.xml: 1533906435
browserconfig.xml: 2943879438
site.webmanifest: 3478304621
Nun müssen wir diese Seitentypen per TypoScript anlegen:
TypoScript-Seitentyp für browserconfig.xml
Die XML-Datei browserconfig.xml
enthält Icon Metainformationen für Windows Geräte. Sie sieht ungefähr so aus:
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="/mstile-70x70.png"/>
<square150x150logo src="/mstile-150x150.png"/>
<wide310x150logo src="/mstile-310x150.png"/>
<square310x310logo src="/mstile-310x310.png"/>
<square144x144logo src="/mstile-144x144.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>
Einen entsprechenden Seitentyp können wir beispielsweise mit der TypoScript Datei EXT:sitepackage/Configuration/TypoScript/Setup/browserconfig.xml.typoscript
wie folgt anlegen:
browserconfig\.xml = PAGE
browserconfig\.xml {
typeNum = 2943879438
config {
disableAllHeaderCode = 1
additionalHeaders.10.header = Content-type:text/xml
admPanel = 0
debug = 0
}
10 = TEXT
10 {
wrap (
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
|
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>
)
value = square70x70, square150x150, wide310x150, square310x310, square144x144
split {
token = ,
cObjNum = 1
1.cObject = COA
1.cObject {
stdWrap.wrap = <|/>
10 = TEXT
10 {
current = 1
trim = 1
noTrimWrap = ||logo |
}
20 = TEXT
20 {
current = 1
trim = 1
replacement.10 {
search = #(square|wide)#i
replace =
useRegExp = 1
}
dataWrap = src="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/mstile-|.png}"
}
}
}
}
}
Über die Angabe typeNum = 2943879438
teilen wir dem System die Type Nummer mit. Darüber hinaus geben wir ein paar Angaben unter config
mit, die die Ausgabe als XML-Dokument ermöglicht.
TypoScript-Seitentyp für site.webmanifest
Die JSON-Datei site.webmanifest
(ggfls. auch manifest.json
genannt → achte auf den Dateinamen im rel="manifest"
-link-Tag) enthält Icon Metainformationen für Android Geräte. Sie sieht ungefähr so aus:
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-36x36.png",
"sizes": "36x36",
"type": "image/png"
},
{
"src": "/android-chrome-48x48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "/android-chrome-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/android-chrome-96x96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "/android-chrome-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-256x256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/android-chrome-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
Einen entsprechenden Seitentyp können wir beispielsweise mit der TypoScript Datei EXT:sitepackage/Configuration/TypoScript/Setup/site.webmanifest.typoscript
wie folgt anlegen:
site\.webmanifest = PAGE
site\.webmanifest {
typeNum = 3478304621
config {
disableAllHeaderCode = 1
additionalHeaders.10.header = Content-type:application/json
admPanel = 0
debug = 0
}
10 = COA
10 {
stdWrap.wrap = {|}
10 = TEXT
10 {
dataWrap (
"name":"{siteLanguage:websiteTitle // site:websiteTitle}",
"short_name":"{siteLanguage:websiteTitle // site:websiteTitle}",
"icons":[|],
"theme_color":"#ffffff",
"background_color":"#ffffff",
"display":"standalone"
)
value = 36x36, 48x48, 72x72, 96x96, 144x144, 192x192, 256x256, 384x384, 512x512
split {
token = ,
wrap = {|},|*|{|},|*|{|}
cObjNum = 1
1.cObject = COA
1.cObject {
10 = TEXT
10 {
current = 1
trim = 1
dataWrap = "src":"{path:EXT:sitepackage/Resources/Public/Icons/Favicon/android-chrome-|.png}",
}
20 = TEXT
20 {
current = 1
trim = 1
wrap = "sizes":"|","type":"image/png"
}
}
}
}
}
}
Auch hier erfolgt wieder die Angabe der Type Nummer typeNum = 3478304621
sowie der config
-Angaben zur Ausgabe einer JSON-Datei.
Die Angaben
"name":"{siteLanguage:websiteTitle // site:websiteTitle}",
"short_name":"{siteLanguage:websiteTitle // site:websiteTitle}",
könnt Ihr ggfls. noch an Eure Bedürfnisse anpassen. Aktuell wird hier einfach der in der Site Configuration verwendete Website Titel (ggfls. sprachenabhängig) verwendet.
Fazit
Fertig, viel Spaß mit den neuen dynamischen App Icons!
Das ist einer von vielen Wegen, wie Ihr die Ausgabe von App Icons in TYPO3 hin bekommt. Das schöne an dieser Lösung ist, dass sich die TypoScript-Dateien per "Copy and Paste" direkt im nächsten Projekt wiederverwenden lassen. Dazu den Generator mit gleichen Grundeinstellungen und neuer Grafik nochmal anwerfen, Bilddateien wieder unter dem oben definierten Pfad ablegen, Site Configuration erweitern, die beiden neuen TypoScript-Dateien ins Sitepackage legen und ggfls. den Extension-Key im TypoScript anpassen.
Einen alternativen Ansatz zur Einbindung der Manifest JSON bietet beispielsweise Sebastian Klein per Middleware in folgendem Gist. Dieser Ansatz ließe sich sicherlich auch übertragen auf die browserconfig.xml
.
Nun steht es Euch frei zu entscheiden welchen Weg Ihr gehen wollt 😉