История коммитов

simba77
refactor(database): move the schema into baseline migrations
The installer no longer builds the tables itself. A fresh installation and an
existing site upgrading to 10.0 now take the same path: the migrations of the core
and of every module. The four places the schema used to be described in - the
installer, AuthSchema, MailSchema and the install() of each module - are gone.

The baseline is a snapshot rather than a step, and the one place idempotence is
allowed: it creates what is missing and leaves the rest alone, so a site upgrading
from 9.9 arrives at the schema a fresh one is built with. Every migration after it is
a plain step forward.

BaselineSchemaParityTest holds the old code to account: the fixture is the exact MySQL
the installer and the module installers produced on a fresh database, recorded from
that code before it was deleted, and the migrations have to produce the same, table by
table. Verified against a real server as well - a database built from the migrations
and the one built by the old installer have the same 60 tables, with the same columns,
keys and options; five tables list their keys in a different order, which MySQL does
not care about.

The port grew two questions the baseline needs to be able to skip its own work:
hasForeignKey(), because a database may satisfy a foreign key with an index it already
has and then no index carries the name of the constraint, and fullText(), the word
index the forum and the library search with - left out on a database that has no such
thing.

AuthSchema and MailSchema also held the names of their tables, which models and queries
spell with them; those names live on in AuthTables and MailTables.

Tests build their schema by running migrations (Tests\Support\RunsMigrations) instead
of calling a second description of the same tables. A test may ask for single
migrations of a source, and for the core it has to: its index names come from 9.x,
where several tables name an index user_id, which MySQL keeps per table and SQLite per
database.
simba77
feat(database): add the migration engine
Migrations live in system/migrations and modules/<module>/migrations, one file per
step returning an anonymous class extending Migration. A migration is given a schema
and a connection and nothing else: it has to keep running unchanged on a site
upgrading years from now, and a model or a service it reached for will have moved on.

The journal is a table of the database it describes, so it travels with a restored
dump instead of being left behind in a config file. A row is identified by its source
and version rather than by a file name: two modules may well reach the same minute,
and the words after the timestamp are a description an author may correct.

Sources come from MigrationSourceProviderInterface, tagged by the container, so a
package that ships tables of its own joins the run without a line changed here. The
core is asked first and the modules after it - a module may point at the tables of the
core and the core points at nobody's.

A run takes a lock, applies one migration at a time and journals each before calling
it done. A failure stops the run and is not journalled, and the message says the step
may have carried out part of its work: MySQL does not roll back DDL.

Commands: migrate, migrate:status, migrate:rollback and make:migration. Rolling back
is a tool of development - it undoes a step the data has already been through, so a
migration that says nothing refuses instead, and a site not in debug mode has to pass
--force.
simba77
feat(database): add the connection and schema ports
Two contracts for the code that has to outlive the ORM: ConnectionInterface for
running a query, and SchemaInterface with a neutral table description for creating
and altering tables. Neither names the library underneath.

The description covers what the CMS actually uses - 19 column types, five modifiers,
four kinds of key - and deliberately leaves out platform-only idioms such as column
positioning, which no portable adapter could carry out.

Behind the ports, IlluminateSchema and BlueprintCompiler translate to the Eloquent
schema builder: the same builder that created the tables of every existing
installation, so a table built through the port is identical to the one there now.
MysqlDdlTest pins that DDL statement by statement, against a connection double that
cannot reach a server.

The description is excluded from the directory load of the container: its value
objects carry a column name and a length, and autowiring them breaks the
compilation.
simba77
fix(admin): autowire the config repositories writing local files
FileCaptchaConfigRepository and FileSystemAuthConfigRepository take a LocalConfigWriter, but both were registered with set() without autowire(), so the constructor argument was never injected and the captcha settings page ended in an ArgumentCountError.
simba77
Merge remote-tracking branch 'origin/10.x' into 10.x
# Conflicts:
# composer.lock
# docs
# system/src/Container/PSRContainerFactory.php
simba77
docs(captcha): bump the documentation submodule
simba77
feat(admin): add the captcha settings page
simba77
feat(captcha): add the recaptcha v3, smartcaptcha and hcaptcha providers
simba77
feat(captcha): introduce a provider contract and update mobicms/captcha to 5.x
simba77
docs(content): bump the documentation submodule
Adds the page describing the content pipeline: how to render a user's text, and how a module adds a step or a media provider.

Corrects a comment left in the news comments controller with it: Twig\Markup implements JsonSerializable, so json_encode() writes it out as a string rather than as an empty object. The cast to string stays — it keeps the type of the field of a JSON payload obvious — but not for the reason the comment gave.