Breaking: #110762 - File storages are configured in a file instead of the database
See forge#110762
Description
A file storage answers the question where TYPO3 reads files from, with
which driver and with which credentials. That is infrastructure, decided when
an installation is set up and usually changed together with a deployment. It
is not content that editors produce. TYPO3 nevertheless stored it as a record
in the database table
sys_ since the File Abstraction Layer
was introduced with TYPO3 6.0, edited through FormEngine like a content
element. What was the pragmatic choice at that time turned into three problems
that the Core of today can solve:
- A storage definition is not for regular admins. It decides which
directories of the server and which remote buckets TYPO3 reads, writes
and deletes in, and which credentials it uses to do so. Changing the base
path of a local storage to a directory outside the web root, or pointing
it at another site on the same server, exposes and destroys data that no
backend permission was ever meant to cover. Still, any backend user with
tables_on the table could edit it, and the shipped editor permission preset granted exactly that. The definition now lives inmodify config/, next tosystem/ settings.php, and is only reachable for whoever maintains that directory: system maintainers, deployment tooling and upgrade code. - A storage definition depends on the environment. Credentials, bucket
names and base paths legitimately differ between local, staging and
production, and belong to the deployment, not to a database dump that is
synchronized between them. A database row cannot reference an environment
variable. The file can:
%envplaceholders and()% importswork as in the site configuration, so the definition is versioned, reviewed and deployed with the project, with the secrets kept outside. - A storage definition starts at the project root. The base path of a
local storage was relative to the public web directory, so a storage
outside the web root, the natural place for files that must never be
served directly, could only be configured with an absolute path that ties
the definition to one server. Relative base paths are now resolved
against the project path, see Feature: #110762 - Project-root relative base paths for local storages:
public/for the default storage of a Composer based installation,fileadmin/ storage/for a directory next toprivate/ config/andvar/.
File storages are therefore configured in the file
config/
(typo3conf/ in classic mode). The database
table
sys_, its TCA and the record form are removed.
There is no backend form to edit the file.
The file is optional. Without it, TYPO3 assumes exactly the storage it
created automatically until now: one local storage with identifier
1
for the directory configured in
$GLOBALS. Installations with
only this storage need no file at all.
storages:
1:
name: fileadmin
2:
name: 'Assets (S3)'
driver: AmazonS3
public: false
configuration:
bucket: '%env(S3_BUCKET)%'
key: '%env(S3_KEY)%'
secret: '%env(S3_SECRET)%'
The map key is the storage identifier, the former
uid
. It stays
numeric because every persisted reference, from
sys_ to
combined identifiers like
1:/, uses it. Do not reuse the
identifier of a removed storage: file mounts, upload folders in TSconfig and
links may still point to it. Each storage takes the following options, all
optional:
| Option | Default | Former column |
|---|---|---|
name
|
Storage <identifier>
|
name
|
driver
|
Local
|
driver
|
default
|
true
for a single storage, else
false
|
is_. Exactly one storage must be the default
when several are configured. |
browsable
|
true
|
is_ |
public
|
true
|
is_ |
writable
|
true
|
is_ |
online
|
true
|
is_. A maintainer decision only, the runtime fuse of
failing drivers lives in the
hash
cache. |
auto |
true
|
auto_ |
processing | empty, meaning _processed_/ inside the storage |
processingfolder
|
configuration
| empty map |
configuration
, the former FlexForm, as a plain map of
driver options |
The file is loaded through the YAML loader of TYPO3, so
%env
placeholders and
imports
work. Placeholders are kept when TYPO3
writes the file itself. Storages defined in an imported file are changed in
that file; TYPO3 refuses to write them into the main file. Comments and YAML
anchors of the main file are not preserved when TYPO3 writes it.
The base path of a local storage is either absolute or relative to the
project path. The former option
path is still
understood and resolves against the public path, new entries should not use
it.
When the option
case is not set, the local driver detects the
behaviour of the file system below the base path.
Reading the file is tolerant, so that a single mistake does not stop every file access of the site. Each problem is logged:
- Unknown options of a storage are ignored, so a file written for a newer TYPO3 version still works.
- A storage with an invalid value, or with an
%envplaceholder whose environment variable is not set, is available as an offline storage.()% - If several storages are marked as default, the first of them is the default storage; if none is, the first storage is.
A YAML syntax error, a non-numeric identifier or a storage that is not a map of options are errors that stop file access. TYPO3 itself validates strictly when it writes the file.
The evaluated file is kept in the
core
cache, keyed by the content of
the file. A changed file takes effect right away; after changing an imported
file or an environment variable used by a placeholder, flush the caches.
Migration of existing records
The upgrade wizard "Move file storage records to
config/system/file-storages.yaml" writes the rows of
sys_
into the file:
- The FlexForm of each record becomes the
configurationmap. Local base paths that were relative to the public directory are rewritten into paths relative to the project path. Theuidstays the identifier. - Deleted records are skipped. If no record is marked as default, the first one becomes the default; if several are, the first of them stays the default.
- An installation whose only record is the auto-created
fileadminstorage with identifier1gets no file. - Records of a table that the database analyzer already renamed to
zzz_are migrated as well.deleted_ sys_ file_ storage - If the file defines storages already, for example because it was committed from another environment, the wizard does not change it. It lists the storages of the database that are missing in the file and stays open until they are added or removed from the database.
- Driver configuration of other drivers than
Localis written as found, credentials included. The wizard points this out.
The file is part of the deployment, so the wizard should run where the result can be committed, usually in the local development environment:
- Run the upgrade wizard.
- Replace credentials in the file with
%envplaceholders and provide the variables in every environment.()% - Commit the file and deploy it.
- Run the wizard on every further environment. It reports storages of that database that are missing in the deployed file.
- Remove the table with the database analyzer.
Production systems usually have no version control and are often
deployed read-only, while their database is the one that holds the storage
records that matter. Do not run the wizard there first. Instead, import a
recent copy of the production database into the local or staging
environment, run the wizard there, and commit and deploy the resulting file
before upgrading production. The wizard on production then only confirms
that the deployed file covers all storages. If the wizard has already
written the file on a production system, copy it from there into the
project before the next deployment replaces config/.
The wizard is only offered when a file is needed. It is not shown when the
table does not exist (anymore), when it holds no active records, or when its
only record is the auto-created fileadmin storage with identifier
1
and unchanged settings. Such installations, the majority, need no
file, no commit and no extra step in their deployment.
Impact
- The database table
sys_, its TCA, the record form and the labels of its columns are removed. Records of the table can no longer be created, edited, exported or referenced by the reference index. The table is dropped by the database analyzer after the upgrade wizard ran. Select fields of other tables that usedfile_ storage foreign_have to use the items processor functiontable => 'sys_ file_ storage' Tcainstead, which lists the storages the current backend user may access.Items Processor Functions->populate File Storages () - The permission presets no longer grant
tables_andselect tables_for the table. Backend users cannot change storages anymore; which storages a user sees is defined by file mounts alone.modify \TYPO3\reads the file instead of the database. Its constructor takesCMS\ Core\ Resource\ Storage Repository \TYPO3\andCMS\ Core\ Resource\ Configuration\ File Storage Configuration \TYPO3\in addition toCMS\ Core\ Resource\ Configuration\ File Storage Configuration Writer ConnectionandPool Flex. The new methodForm Tools addappends a local storage to the file.Local Storage () createkeeps its signature, writes the file as well and is deprecated, see Deprecation: #110762 - StorageRepository->createLocalStorage() and legacy keys.Local Storage () \TYPO3\receives the normalized storage definition instead of a database row: the keys areCMS\ Core\ Resource\ Resource Storage identifier,name,driver,default,browsable,public,writable,online,auto,Extract Metadata processingandFolder configuration(always an array). The same array is passed to\TYPO3\and returned byCMS\ Core\ Resource\ Event\ Before Resource Storage Initialization Event get. Listeners that set the keys of the former row are mapped for one major version and trigger a deprecation, see Deprecation: #110762 - StorageRepository->createLocalStorage() and legacy keys. Listeners that read the former keys fromRecord () getget nothing and have to be adapted.Record () getis deprecated, see Deprecation: #110762 - ResourceStorage->getStorageRecord().Storage Record () Resourceno longer writes any record. It marks the storage as offline in theStorage->mark As Permanently Offline () hashcache until the caches are flushed. A storage that has to stay offline is configured withonline: false.\TYPO3\is removed and theCMS\ Core\ Resource\ Driver\ Driver Registry->add Drivers To TCA () flexkey ofForm DS $GLOBALSis no longer evaluated. A driver that still registers one triggers a deprecation log entry pointing to this document. Driver registration itself is unchanged: drivers keep registering a class, a short name and a label. The driver of a storage receives the['TYPO3_ CONF_ VARS'] ['SYS'] ['fal'] ['registered Drivers'] configurationmap of the file as its configuration array, exactly as it received the parsed FlexForm before.- The FormEngine node
userand the classSys File Storage Is Public \TYPO3\are removed, as is the methodCMS\ Backend\ Form\ Element\ User Sys File Storage Is Public Element Abstract.Item Provider->remove Items By User Storage Restriction () - Storage root nodes of the file tree now carry
recordType sys_andfile resourceType storage; their context menu offers the folder actions instead of the record actions of the former storage record. - The fallback storage with identifier
0is not part of the file and stays as it is; it is scheduled for removal separately.
Affected installations
Every installation. Installations with only the auto-created
fileadmin storage work without any change. Installations with
further storages have to run the upgrade wizard, after which the table can
be dropped. Extensions that read or write
sys_ records
directly, construct
Resource objects from database rows, read
the former keys in a listener of
Before or provide a
flex for their driver are affected. The extension scanner
reports the removed and changed methods.
Migration
Run the upgrade wizard as described above, then let the database analyzer
remove the table. Edit config/ by hand for
any further change, or use
File from
command line tools and set up code. Writing the file during regular web
requests is not supported: it fails on read-only file systems and diverges
between several web servers. Replace database access to
sys_ with
Storage, which offers
find,
find,
get and
find. Driver extensions
delete the
flex entry and document their options for the
configuration
map instead.