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

.
docs: bump the documentation submodule
Points at docs(modules): correct the module guides against the code.
.
fix(view): resolve module templates by alias and manifest path
The Twig namespace of a module was basename() of its key, and its templates
were looked for under MODULES_PATH . <key>. The alias therefore did nothing
for templates — it only looked as if it worked, because every module of the
release is called what its directory is called — and a module Composer left in
vendor/ got no namespace at all: its manifest, services, routes, translations
and migrations were read from vendor/, while its templates were expected in a
modules/ directory that does not exist. Nothing moves a Composer package into
modules/; only an archive is unpacked there.

Both now come from the manifest: the namespace is the alias, the directory is
the one the manifest was read from. A theme keeps overriding such a template
under themes/<theme>/templates/<alias>/, and a page the theme does not carry
still falls back to the package.
.
docs: bump the documentation submodule
Points at docs(upgrade): login and registration are one module in 10.0.
.
refactor(auth): merge login and registration into one module
Both were thin screens over system/src/Auth with no tables of their own, and
signing in and signing up are one journey for one visitor. They now live in
modules/johncms/auth, together with the guest screens that sat in the profile
module although whoever opens them has no session.

- Login and registration keep their URLs. The recovery and address-confirmation
screens move to /password-recovery, /password-recovery/{id}/{code} and
/confirm-email/{id}/{code}; the old /profile/* addresses answer with a
permanent redirect, because links already sent by e-mail point there.
- RegistrationSettings moves to the core. The admin panel no longer depends on
the registration module for it, and the external sign-in flow stops reading
the raw config key, which missed the mod_reg fallback and let a site that
never saved its settings register unmoderated accounts through a provider.
- The module carries its own AuthUserRepositoryInterface rather than the
profile's, so the screens work on a site with profiles switched off;
findByNameLat() and confirmNewEmail() leave ProfileUserRepositoryInterface
with nobody to call them.
- Translation domain registration -> auth. The strings of the login screens
leave the system domain and the recovery ones the profile domain, carrying
their existing translations for every language that had them.
.
fix(admin): a section of a module is a page of the panel too
The news section rendered the whole admin menu in the source English. The
panel domain was entered by the access guards of the admin module, and a
section guarded by a permission of its own — news, collections — never went
through them. The same awaited any third-party module with a section: not a
bug of the news module but a hole in the contract.

Routes now say it: ->adminArea(), on a route or on a group, the way
->permission() already works. The pipeline enters the context from that
attribute, right after the module context and before any middleware, so a
page gets the translations of the panel and the first link of its
navigation chain whichever module it belongs to.

The guards stop entering it themselves — with the pipeline doing it, the
"Админ панель" link would be added twice.

Two things this turned up. A nested group inherits nothing from the group
around it, middleware included, so the super-admin group needed the mark of
its own: without that, 51 routes of /admin/settings and /admin/ip-bans lost
the context the guards used to give them. And consent and contacts relied
on AdminAccessMiddleware for it, so removing it from there would have fixed
the news section while breaking those two — both are marked as well.

Verified by walking every route under /admin: none is left unmarked.
.
i18n(ru): translate the modules section
The 32 strings the modules section brought, plus five in the system domain
that had been sitting untranslated: the permission to install modules, the
names of the captcha services and the hint under the image code.

Wording follows what the screens actually do rather than the English
literally — "Switched on/off" for a module is "Включён/Выключен", and the
warning before a purge says outright that it cannot be undone.

Also fixes something the suite was quietly depending on. The functional
tests drive real requests through the real registry, which reads the state
file of the site — so a developer who had switched a module off on their
own installation would see unrelated tests fail with a 404. The test
bootstrap now points the registry at a state file that does not exist,
which falls back to the modules of the release: what a fresh site has, and
what CI already ran against.
.
docs(migrations): the source of a module is its alias
Three things the module work changed about migrations, none of them written
down until now: the source is the alias rather than the directory, a
switched-off module keeps its source because its tables are still there,
and one that was never installed has none at all. Plus the rollback that
undoes a whole source, which uninstalling a module with --purge is built
on.

Also bumps the documentation submodule: the two module pages.
.
feat(admin): background tasks with arguments
A queued task is run later, by the scheduler, in another process — so what
it needs to know has to travel with it. "Install a module" is not a task
until it says which module.

The state of a task now carries its arguments, getQueued() returns those
states rather than bare command names, and the runner passes them to
ArrayInput. AsAdminTask grew a "listed" flag: a task that needs an argument
has nothing to offer a screen of buttons, so it stays off the maintenance
page while remaining queueable from wherever that argument comes from.

The consumer is real, which is why this was worth doing at all: the
confirmation screen of a module operation now offers "run in the
background", and install, update and uninstall go to the scheduler with the
module key and the --demo / --purge flags. A module with heavy migrations
does not fit into the time a web request is given on a modest host.
Switching a module on or off stays synchronous — that is a line in a file.

Verified end to end: queued with an argument, picked up by
admin-tasks:run-queued, migration applied, module installed, output kept in
the state of the task.
.
feat(modules): composer as a delivery channel
A module distributed to other sites is an ordinary package with
"type": "johncms-module", installed with composer require. The CMS finds it
by asking the Composer runtime which packages of that type are installed —
InstalledVersions::getInstalledPackagesByType(), already in the vendor
directory — so the module simply stays where Composer put it.

That is not what the plan said. It called for composer/installers plus
oomphinc/composer-installers-extender, which move a package of a custom
type into modules/{$vendor}/{$name}/. Both were installed and both worked,
but Composer reports the extender as abandoned with no replacement, and
without it the custom type means nothing and the package lands in vendor/
anyway. An abandoned dependency in the core, to move a directory, is a bad
trade. Both are gone again.

So a module can now come from two places, and ChainModuleRepository puts
them behind one answer: modules/ first, vendor/ second. Nothing above it
knows the difference — the manifest carries the path.

The hooks only speak. After installing a package they print the command to
finish with and note the key; before removing one they warn that the tables
and the data stay and point at module:uninstall --purge. The previous
attempt at this booted the whole application inside the Composer process
and ran migrations from there, which meant composer install on a developer
machine migrating whatever database the configuration pointed at.

Verified against a real package through a path repository: require put it
in vendor/vasya/blog, module:list saw it as discovered, module:install
finished the job, and composer remove printed the warning.
.
chore(i18n): regenerate the templates after the move under a vendor
The .pot files still pointed at modules/<name>/… for every string in them:
the templates were last generated before the modules moved under their
vendor. Regenerating rewrites those references and picks up the strings of
the modules section along the way.

Nothing but source references and the new strings changed; no .po was
touched, so no translation was lost.