[FEATURE] TYPO3 v14 compatibility - #282
Conversation
|
Hi, one mark from my side: the PHP version constraint in the composer.json should be "^8.2" instead of "^8.4", because TYPO3 14.3 allows PHP 8.2 until PHP 8.5, see https://packagist.org/packages/typo3/cms-core |
|
Found another issue in PageContentPreviewRenderingEventListener.php: |
Review feedback (thanks @Kephson): - Lower PHP requirement to ^8.2 to match typo3/cms-core ^14.3 and remove PHP 8.4-only constructs (new without parentheses in FileTreeStateController, array_any() in CheckPermissions) - Point rector.php at PHP 8.2 so the 8.4-only syntax is not re-introduced on future rector runs - getFieldFromFlexform(): replace the is_array()/isset() chain with a single null-coalescing lookup to avoid undefined array key warnings - Guard against xml2array() returning an error string for empty or invalid pi_flexform values (TypeError on the typed array property) Additionally, add two changes that were described in the PR summary but missing from the initial commit: - Restrict the public url rewrite to the BE dump_file route to backend context, so frontend urls are generated by the TYPO3 core (eID=dumpFile) and secured downloads work for website visitors - Sign eID download tokens in DownloadLinkViewHelper with SHA3-256 as required by the TYPO3 v14 FileDumpController
|
Hi @Kephson, thanks a lot for the review — both points were valid and are fixed in the latest commit. PHP constraint: You're right, typo3/cms-core ^14.3 allows PHP 8.2–8.5, so I lowered the requirement to "php": "^8.2". Note this needed two code changes as well, since the branch contained PHP 8.4-only constructs that would fail on 8.2/8.3:
I also pointed rector.php at PHP 8.2 so a future rector run doesn't re-introduce them. getFieldFromFlexform(): Good catch. I went one step further than swapping isset()/is_array(), because the earlier is_array($flexform[$sheet]) / is_array(...['lDEF']) checks would still emit "Undefined array key" warnings when the sheet is missing. The whole condition is now a single warning-free lookup: $value = $this->flexformData['data'][$sheet]['lDEF'][$key]['vDEF'] ?? null; The is_string() check also protects the ?string return type in case vDEF ever holds an array. While touching this, I fixed one more latent issue in the same listener: GeneralUtility::xml2array() returns an error string for empty/invalid pi_flexform (e.g. a freshly inserted, never-saved plugin element), which would have caused a TypeError on the typed array $flexformData property — the result is now guarded with is_array(). One more thing: while preparing this update I noticed two changes that were described in the PR summary but accidentally missing from the initial commit — the backend-context restriction of the dump_file URL rewrite and the SHA3-256 signing of the eID download tokens. Both are included in this commit now, so the code matches the PR description. Sorry for the confusion, and thanks again for taking the time to review! |
Summary
This PR makes the extension compatible with TYPO3 v14 (tested on 14.3.4) and PHP 8.4.
Besides migrating all APIs that were removed in v13/v14, it fixes two functional bugs that
already exist on
master(details under "Bug fixes"): the public-URL event listener neverwrites the generated URL back to the event, and — once that is fixed — the backend
ajax_dump_fileURL leaks into frontend output because the oldTYPO3_MODE === 'BE'guardwas lost in the PSR-14 migration.
Compatibility
^14.3(see "Open questions":^14.0probably works, only tested on 14.3.4)^8.41. Migration of removed core APIs
Page module preview hook →
PageContentPreviewRenderingEvent$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['list_type_Info']was removed in v12 (Breaking: #98375),
so the plugin info in the page module has silently not been rendered since v12.
Hooks/CmsLayout.phpis replaced byEventListener/PageContentPreviewRenderingEventListener.php,which matches on the
falsecuredownload_filetreeCType (plugins are CType-based sinceDeprecation: #105076;
the existing
BeechItFalSecuredownloadCTypeMigrationupgrade wizard covers existing records).The listener reads
pi_flexformfrom the event's raw record and usesStorageRepository::findByUid()instead ofResourceFactory::getStorageObject(), which wasremoved in v14 (Important: #107735).
ButtonBar hook →
ModifyButtonBarEvent$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['Backend\Template\Components\ButtonBar']['getButtonsHook']was removed in v12 (Breaking: #96806),
so the "folder permissions" button in the file list doc header has been missing since v12.
Hooks/DocHeaderButtonsHook.phpis replaced byEventListener/ModifyButtonBarEventListener.php(still based on
Hooks/AbstractBeButtons). Buttons are created via the newComponentFactorysinceButtonBar::makeLinkButton()is deprecated in v14(Deprecation: #107823).
Context menu item provider
$GLOBALS['TYPO3_CONF_VARS']['BE']['ContextMenu']['ItemProviders']registration and the v11-only constructor arguments; providers are auto-configured via DI
since v12 (Breaking: #96333).
In v14
AbstractProviderhas an argument-less constructor and receives its context viasetContext().getAdditionalAttributes()and deleted the AMD moduleResources/Public/JavaScript/ContextMenuActions.js(the ES6 modulecontext-menu-actions.jsis used since v12).Fluid 5 / ViewHelpers
TYPO3 v14 ships Fluid 5 (Breaking: #108148).
In
DownloadLinkViewHelper:registerUniversalTagAttributes()/registerTagAttribute()were removed(Deprecation: #104223);
unregistered arguments are applied as tag attributes automatically in Fluid ≥ 4.
parent::__construct()(initializes theTagBuilder).$GLOBALS['TSFE']->absRefPrefixwas removed together withTypoScriptFrontendController(Deprecation: #105230,
removed in Breaking: #105377).
config.absRefPrefixis now read from thefrontend.typoscriptrequest attribute of theFluid rendering context.
Icon API
ModifyIconForResourcePropertiesEvent::getSize()was replaced bygetIconSize()returningthe
IconSizeenum (Deprecation: #101475,removed in Breaking: #105377).
IconFactoryAspect::buildIconForResource()now type-hintsIconSizeand accepts?string $iconIdentifier(the event getter is nullable).ContentObjectRenderer
GeneralUtility::makeInstance(ContentObjectRenderer::class, null)fatals in v14 (theconstructor requires DI services).
ModifyFileDumpEventListener::resolveUrl()now resolvesthe instance from the container and passes the PSR-7 request via
setRequest()sotypolinkworks in the eID context.Upgrade wizard
The
UpgradeWizardattribute andAbstractListTypeToCTypeUpdatemoved from EXT:install toEXT:core in v14 (Deprecation: #106947).
Imports were updated and the
typo3/cms-installrequirement was dropped fromcomposer.json— the extension no longer references any EXT:install class.Misc
ext_localconf.php: removed a leftoverGeneralUtility::makeInstance(IconRegistry::class)call — instantiating
IconRegistryinext_localconf.phpthrows aRuntimeExceptiononevery request in v14 (icons are registered via
Configuration/Icons.php).Configuration/FlexForms/FileTree.xml: removed the invalid<internal_type>db</internal_type>option from the
settings.storageselect field.Typo3Versionchecks) intx_falsecuredownload_folder.phpandOverrides/sys_file_metadata.php.Classes/Service/UserFileMountService.php: its parent classTYPO3\CMS\Core\Resource\Service\UserFileMountServiceno longer exists in v14 and nothingreferences it (the flexform folder selector uses
type=foldersince v12).ext_emconf.php:dependsraised to14.3.0 - 14.99.99; removed the obsoleteclearCacheOnLoadoption.rector.phpwith the TYPO3 14 rule set (optional, see PR notes).2. Bug fixes
Public URLs of secured files pointed to the backend route in the frontend (or were lost)
Historically the
ResourceStoragePreGeneratePublicUrlslot was registered only inbackend mode (
if (TYPO3_MODE === 'BE')inext_localconf.php) so that backend editorsget working preview links (
/typo3/ajax/fal_securedownloads/dump_file) in the file list.The migration to the PSR-14
GeneratePublicUrlForResourceEventintroduced two bugs thatmask each other on current
master:['publicUrl' => ...]by value toPublicUrlAspect::generatePublicUrl()and never calls$event->setPublicUrl(), so thegenerated URL is silently discarded. (In the signal/slot era this worked because the
dispatcher passed an array containing a reference.)
TYPO3_MODE === 'BE'guard was reduced to!Environment::isCli(), so once bug 1 isfixed, every frontend rendering of a file from a non-public storage (file links,
images, processed files) points to the backend AJAX route and returns 401 for website
visitors.
Fix in this PR:
PublicUrlAspect::generatePublicUrl()takes$urlDataby reference and the listenerwrites the result back via
$event->setPublicUrl().ApplicationType::fromRequest(...)->isBackend(). In frontendcontext it sets no URL, which lets TYPO3 core generate its standard
index.php?eID=dumpFile&...&token=...URL — and downloads through that endpoint arepermission-checked by this extension's
ModifyFileDumpEventListener, exactly as intended.DownloadLinkViewHelpergenerated invalid tokens for TYPO3 v14TYPO3 v14 hardened HMAC generation to SHA3-256
(Breaking: #106307).
FileDumpControllervalidates theeID=dumpFiletoken withHashAlgo::SHA3_256, while the ViewHelper still signed with the SHA-1 default — every linkproduced by
<sd:downloadLink>was rejected with 403. The ViewHelper now signs withHashAlgo::SHA3_256.(The extension-internal pair
PublicUrlAspect/BePublicUrlControlleruses its ownBeResourceStorageDumpFiletoken on both sides and is unaffected.)3. File overview
Classes/Hooks/CmsLayout.php→Classes/EventListener/PageContentPreviewRenderingEventListener.phpClasses/Hooks/DocHeaderButtonsHook.php→Classes/EventListener/ModifyButtonBarEventListener.phpClasses/Service/UserFileMountService.phpResources/Public/JavaScript/ContextMenuActions.jsext_localconf.php,ext_emconf.php,composer.json,Configuration/Services.yaml,Configuration/FlexForms/FileTree.xml,Configuration/TCA/*,Classes/Aspects/*,Classes/ContextMenu/ItemProvider.php,Classes/EventListener/*,Classes/ViewHelpers/DownloadLinkViewHelper.php,Classes/Updates/BeechItFalSecuredownloadCTypeMigration.php,Resources/Public/JavaScript/context-menu-actions.js4. How this was tested
Manually on TYPO3 14.3.4 / PHP 8.4 (composer mode, ddev):
non-public storage secured via folder permissions.
eID=dumpFilelinks; download attempt returns403 "Authentication required!".
processed image download/stream correctly (200, correct MIME types).
overlay icons; folder context menu and doc-header "folder permissions" button work.
cache:flush,cache:warmup, scheduler runs without errors (DI container compiles,ext_localconf/TCA load cleanly).
Not covered: automated tests (the repository currently has none), ke_search / solrfal
integrations (interfaces unchanged).
5. Open questions for maintainers
7.0.0(v14-only). If you want to keepa v13-compatible line, the two bug fixes in section 2 apply to
masteras well (bug 1verbatim; bug 2's guard is also correct for v12/v13).
composer.jsoncurrently requires^14.3because that is what this wastested against; all migrated APIs exist since 14.0, so
^14.0is likely fine.ext_emconf.phpconstraints.depends.typo3was set to14.3.0 - 14.99.99accordingly.