Migration to typo3/composer-cms-installers version 4+
/ updated on
TYPO3 v11
TYPO3 v12
With the availability of typo3/cms-composer-installers version 4 (compatible with TYPO3 v11) and version 5 (mandatory since TYPO3 v12.0), saving extensions in the typo3conf
folder has gone. The article TYPO3 and Composer — we've come a long way describes the changes. For more insight into the change, see another blog post "Composer Changes for TYPO3 v11 and v12". In this blog post I want to show the challenges I had to tackle to make my projects compatible with the new typo3/cms-composer-installers version.
The visible change is that assets are now embedded in the web page without the name of the extension being exposed. In typo3/cms-composer-installers before version 4 it looked like this:
<link rel="stylesheet" href="/typo3conf/ext/site/Resources/Public/Css/styles.css" media="all">
As with version 4, the typo3conf
folder no longer contains the extensions. Instead, the extensions' Public/Resources
folders are linked into a new _assets
folder under a folder which contains a hash value instead of the extension name:
<link rel="stylesheet" href="/_assets/0304760f2d5b5a1f133ce43f8d460a02/Css/styles.css" media="all">
Important: This also means that every asset that is available on a website must be stored in the Resources/Public
folder of an extension.
Therefore, every hard-coded asset path that uses typo3conf/ext
needs to be adapted to be compatible with the new version. So i looked for typo3conf/ext
paths in a project's custom extensions and substituted them over time with the solutions described below. Then I added the typo3/cms-composer-installers release candidate version as a requirement to the composer.json
file of the project.
CSS files
In CSS files, it is common to reference background images with a absolute path, for example:
.selector {
background-image: url("/typo3conf/ext/site/Resources/Public/Images/background.jpg");
}
This can be changed to a relative path. In this example it is assumed that the CSS
folder and the Images
folder both directly under Resources/Public
of the same extension:
.selector {
background-image: url("../Images/background.jpg");
}
If it is only a small image, like an icon, it can also be embedded inline with a data URI:
.selector {
background-image: url("data:image/jpeg;base64,...");
}
Fluid templates
I often use SVG sprites in my projects with a path to a file in typo3conf/ext
:
<svg><use xlink:href="/typo3conf/ext/site/Resources/Public/Images/icons.svg#symbol"></use></svg>
The solution now is to use the URI resource view helper:
<!-- If asset is stored in the same extension -->
<svg><use xlink:href="{f:uri.resource(path: 'Images/icons.svg#symbol')}"></use></svg>
<!-- If asset is stored in another extension -->
<svg><use xlink:href="{f:uri.resource(path: 'Images/icons.svg#symbol', extensionName: 'site')}"></use></svg>
Of couse, the URI resource view helper can also be used to refer to other files in extensions.
TypoScript configuration
In TypoScript I preload the relevant fonts to avoid FOUT, for instance:
page.headerData.10 = TEXT
page.headerData.10 {
value = <link rel="preload" href="/typo3conf/ext/site/Resources/Public/Fonts/Roboto.woff2" as="font" type="font/woff2" crossorigin="anonymous">
}
With the help of the stdWrap function insertData it can be rewritten to:
page.headerData.10 = TEXT
page.headerData.10 {
value = <link rel="preload" href="{path : EXT:site/Resources/Public/Fonts/Roboto.woff2}" as="font" type="font/woff2" crossorigin="anonymous">
insertData = 1
}
Another example is adding the OpenGraph image as meta tag, which must be an absolute URL:
page.meta {
og:image = https://example.com/typo3conf/ext/site/Resources/Public/Images/opengraph.png
}
The solution is to use the typolink function in combination with the content object IMG_RESOURCE:
page.meta {
og:image.cObject = TEXT
og:image.cObject {
typolink {
parameter.cObject = IMG_RESOURCE
parameter.cObject.file = EXT:site/Resources/Public/Images/opengraph.png
returnLast = url
forceAbsoluteUrl = 1
}
}
}
TSconfig configuration
I organise the settings of the backend user groups into files and then reference the file in the TSconfig field of the corresponding backend user group record:
<INCLUDE_TYPOSCRIPT: source="FILE:typo3conf/ext/site/Configuration/TSconfig/User/editors.tsconfig">
This can easily be adapted to be compatible with the new version of typo3/cms-composer-installers:
<INCLUDE_TYPOSCRIPT: source="FILE:EXT:site/Configuration/TSconfig/User/editors.tsconfig">
RTE configuration
In the configuration for the rich text editor, I added a custom CSS file this way:
editor:
config:
contentCss: '/typo3conf/ext/site/Resources/Public/Css/rte.css'
As you've already guessed, using of the EXT:
syntax is the way to go:
editor:
config:
contentCss: 'EXT:site/Resources/Public/Css/rte.css'
JavaScript
Data attributes
Sometimes JavaScript code contains references to files. The paths to the files were hard-coded into the JavaScript file. In this example it was a Leaflet application that defines custom marker icons for a map:
const icon = L.icon({
iconUrl: '/typo3conf/ext/site/Resources/Public/Icons/Map/marker.svg',
iconSize: [25, 41],
iconAnchor: [12,41],
popupAnchor: [1, -34],
tooltipAnchor: [16, -28],
shadowUrl: '/typo3conf/ext/site/Resources/Public/Icons/Map/shadow.svg',
shadowSize: [41, 41],
});
We can solve this by defining the paths to the icons in data attributes in HTML using the Fluid URI resource view helper:
<div id="map"
data-icon="{f:uri.resource(path: 'Icons/Map/marker.svg')}"
data-shadow="{f:uri.resource(path: 'Icons/Map/shadow.svg')}"
></div>
Now we can retrieve the paths to the icons in JavaScript:
const mapElement = document.getElementById('map');
const icon = L.icon({
iconUrl: mapElement.dataset.icon,
iconSize: [25, 41],
iconAnchor: [12,41],
popupAnchor: [1, -34],
tooltipAnchor: [16, -28],
shadowUrl: mapElement.dataset.shadow,
shadowSize: [41, 41],
});
Path substitution
Assuming your script is located in EXT:site/Resources/Public/JavaScript/some-script.js
you may use document.currentScript.src
to create a proper (pseudo-relative) path:
const prefix = document.currentScript.src.replace(/\/JavaScript\/.*\.js.*/, '');
const image = `${prefix}/Images/some-image.png`;
Webpack Encore
Take a look at the documentation of the typo3_encore extension by Sebastian Schreiber to learn how to configure it to use typo3/cms-composer-installers version 4.
PHP
In a project I have a logo provider class that returns the URL to the company logo that is used in several places for embedding structured data:
<?php
declare(strict_types=1);
namespace Acme\Site\Provider;
use TYPO3\CMS\Core\Utility\GeneralUtility;
final class LogoProvider
{
private const PATH_LOGO = 'typo3conf/ext/site/Resources/Public/Images/logo.png';
public static function getLogo(): string
{
return GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . self::PATH_LOGO;
}
}
This can be rewritten as:
<?php
declare(strict_types=1);
namespace Acme\Site\Provider;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
final class LogoProvider
{
private const PATH_LOGO = 'EXT:site/Resources/Public/Images/logo.png';
public static function getLogo(): string
{
return rtrim(GeneralUtility::getIndpEnv('TYPO3_SITE_URL'), '/')
. PathUtility::getAbsoluteWebPath(GeneralUtility::getFileAbsFileName(self::PATH_LOGO));
}
}
Summary
Since version 4 of typo3/cms-composer-installers core and custom extensions are stored in the vendor
folder. The Resources/Public
folders of extensions are linked into the _assets
folder of the web root, with a hash as the folder name to disguise the extension name. Some migrations need to be done to be compatible with the new version. As long as a stable version is not available (as of writing of this blog post only a release candidate was released) it can be explicitly installed in a TYPO3 v11 installation via:
composer req typo3/cms-composer-installers:^4.0-rc
To avoid installing version 4 after the stable version has been release, you can explicitly pin the Composer plugin to version 3 in your project:
composer req typo3/cms-composer-installers:^3.1
But keep in mind: TYPO3 v12 will require the use of of the follow-up version 5, so be prepared.