Skip to content

Migrate Language component to the Component Revision architecture and introduce Activities - #12090

Open
c-knof wants to merge 29 commits into
ILIAS-eLearning:trunkfrom
kroepelin-projekte:language-component-revision
Open

c-knof wants to merge 29 commits into
ILIAS-eLearning:trunkfrom
kroepelin-projekte:language-component-revision

Conversation

@c-knof

@c-knof c-knof commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Migrates the Language component to the Component Revision architecture ($define/$implement/$use/$contribute/$seek/$provide/$pull/$internal) and introduces Activity implementations for its domain operations, replacing logic previously embedded directly in the GUI classes.

  • Introduce 7 Language Activities (components/ILIAS/Language/src/Activities): InstallLanguage, UninstallLanguage, UpdateLanguage, AddLanguageEntry, RemoveLocalLanguageChanges, SetLanguageDetectionEnabled, SetLanguageTranslationEnabled, plus supporting traits/classes (GrindsFormInput, GrindingInputAccess, ParsesLanguageKeyList, ResolvesLanguageKeysToObjIds, RendersActivityErrors, exception types)
  • Extract corresponding domain logic out of class.ilObjLanguageFolderGUI.php / class.ilObjLanguageExtGUI.php into these Activities
  • Add InstalledLanguageRepository/InstalledLanguageDatabaseRepository and LanguageInstallationManager/LanguageFileParsing under src/Setup to back the new Activities and setup objectives
  • Add ComponentTranslation support (ComponentLanguageFileDirectory, CustomizingLanguageFileDirectory, MainLanguageFileDirectory, LanguageFileDirectoryManager) so individual components can ship their own language files instead of everything living in the central lang/ files; implemented exemplarily for the File component (components/ILIAS/File/File.php, components/ILIAS/File/lang/ilias_{de,en}.lang) as a reference for how other components can adopt this going forward
  • Replace several SELECT * FROM lng_modules queries with queries selecting only the needed columns (e.g. SELECT lang_array FROM lng_modules) as part of the lng_modules access rewrite in class.ilSetupLanguage.php / class.ilObjLanguage.php
  • Wire the new dependency graph in Language.php, Init.php, and AllModernComponents.php
  • Extend README.md and docs/development/language.md with the Activities overview and documented, known deviations from the Activity contract
  • Add extensive PHPUnit coverage for every new Activity, the setup repository/manager, the component dependency graph, and GUI call sites

Other changes

  • ilCertificateDateHelperTest.php: loosen the lng_modules query mock match (SELECT * FROM lng_modules → FROM lng_modules), since the column list actually queried against lng_modules changed as part of this migration (see above) and the previous exact-string match no longer applied.

@c-knof c-knof self-assigned this Sep 17, 2026
@c-knof c-knof added improvement php Pull requests that update Php code authorities Pull requests that contain updates to the authorities of a component. labels Sep 17, 2026
@matthiaskunkel matthiaskunkel removed the authorities Pull requests that contain updates to the authorities of a component. label Oct 3, 2026
@matthiaskunkel

Copy link
Copy Markdown
Member

I removed the ‘Authorities’ label because this is not a PR that should change authority assignments in the maintenance.md.

@c-knof
c-knof force-pushed the language-component-revision branch from 9b99a5f to 1d1950c Compare October 12, 2026 15:53
@c-knof
c-knof force-pushed the language-component-revision branch from 6f12808 to db2fc38 Compare October 16, 2026 12:28
@c-knof

c-knof commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current trunk to resolve a conflict in components/ILIAS/File/File.php with the new File Activities (4eeebdf). The contribution of the language directory now sits below the Activity contributions; no other changes.

@c-knof

c-knof commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Aligns the Activities of the Language component with the File Activities that were merged in 4eeebdf:

  • Errors carry status codes: a denied permission is a DomainException with 403, invalid input 400, ambiguous language titles 409. Everything else is masked as a generic 500 with the original as previous, so callers other than the GUI no longer see internal messages (also for PDOExceptions, whose SQLSTATE string would otherwise compare as 4xx).
  • Unknown and anonymous users are refused before RBAC is asked; the trusted user id is passed to perform() directly.
  • Descriptions use ILIAS\Data\Factory, inputs are titled sections, and all legacy access goes through one RepositoryProvider instead of closures in every Activity.

Kept on purpose: the input is still ground through getInputDescription() as the Activities README asks, and forSetup() stays for the Setup. No callers outside the Language component are affected.

@c-knof

c-knof commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor Author

New commits on top of the reviewed state (271e5617014..dd29d5ec2b0), all within the Language component:

  • f29b291 Protect the table actions of ilObjLanguageFolderGUI against CSRF: the table actions ran without a command, so ilCtrl never checked a token. The class now implements ilCtrlSecurityInterface, runs them through the unsafe command executeTableAction and dispatches an exact allowlist (ilCtrl compares commands case-sensitively, PHP calls methods case-insensitively). Requires the ctrl security artifact to be rebuilt (composer dump-autoload / setup build).
  • 4151a27 [FIX] Load ilLangDeprecated through the autoloader (listing deprecated entries failed with an error).
  • 12d8b93 Remove the unreachable confirmation screens from ilObjLanguageFolderGUI.
  • c4c65f3 Pass the previous exception positionally in GrindsFormInput (no named arguments).
  • e07cf87 Expect the calls whose arguments the tests check (PHPUnit 14 deprecations).
  • dd29d5e Mark the internal settings, cache and installation root types as @internal.

@c-knof
c-knof force-pushed the language-component-revision branch from dd29d5e to 7bc37a8 Compare October 23, 2026 07:48
c-knof added 10 commits October 23, 2026 16:49
The seven Activities now get `ILIAS\AccessControl\PublicInterface\RBAC` via `$use` instead of
reaching into `$GLOBALS['DIC']->rbac()`, and call `system()` only in `isAllowedToPerform()`.
The Setup agent uses `InstallLanguage::forSetup()`/`UpdateLanguage::forSetup()`, since Setup
checks no permissions and needs no RBAC.
`InstalledLanguageDatabaseRepository`, `LanguageInstallationManager`, `AddLanguageEntry` and the
`ilSetupLanguage` of the web Activities now get `ILIAS\Database\Connection` via `$pull` instead of
resolving `$GLOBALS['ilDB']`. `ilSetupLanguage` takes an optional connection; Setup keeps handing
in its database via `setDbHandler()`, also for the metrics, and the file-only checks need no
database.
The Setup CLI asks `ilLanguagesInstalledAndUpdatedObjective` for its label before `achieve()`,
so `getLabel()` only worked through a leftover `$GLOBALS['ilDB']`. `isApplicable()`, which the
CLI calls first, now reads the languages from the Setup database and `getLabel()` lists them
without database access.
The repository and the installation manager in the component graph now get the root from
`ILIAS\Environment\Configuration\Installation\Directories` at resolve time instead of a path
computed in `init()`. Callers outside the graph keep passing the path themselves.
… container

`AllModernComponents` bound `$DIC[ILIAS\Language\Language::class]` to the `$use`d language,
whose legacy adapter itself delegates to `$DIC->language()`. Nothing reads the key, so the
constructor parameter and the binding are removed.
No component pulls `InstalledLanguageRepository` or `LanguageInstallationManager`, so they stay
internal services of the component; `ilSetupLanguage` remains the entry point for callers
outside it.
The Activities took the ref_id of the language folder as `int|\Closure` defaulting to 0. They
now require `ILIAS\Language\LanguageFolder`, whose `LanguageFolderLegacyProxy` looks the ref_id up
only when a permission is checked.
Keep only what the code does not say itself (the why, a constraint or a
pitfall), mostly in one line, and drop the history of how it got there.
c-knof and others added 19 commits October 23, 2026 16:49
…vities

A denied permission is now a `DomainException` with code 403 instead of a localized string, and
`Failure::of()` masks every error outside the 4xx range as a generic 500, so callers other than the
GUI no longer see internal messages. `InvalidInputException` carries 400,
`AmbiguousLanguageTitleException` 409; `RendersActivityErrors` decides by code, which replaces the
`SafeToDisplayActivityError` marker.
…the File Activities

`LanguageActivity::isAllowedToPerform()` now rejects unknown and anonymous users before asking RBAC,
as `FileUpload\Activities\AccessCheck` does for the File Activities. `maybePerformAs()` hands the
trusted user id to `perform()` directly, which replaces the `additionalPerformParameters()` hook.
…ections like the File Activities

The descriptions are built with `ILIAS\Data\Factory` instead of an own markdown helper, so the
Activities no longer need the Refinery. The input of every Activity is a titled `section` instead of
an untitled `group`, as in the File Activities.
…Provider` like the File Activities

The language objects, language entries, the module cache, the user lookup and the settings are
reached through one `RepositoryProvider` instead of optional closures with legacy defaults in every
Activity. `UninstallLanguage`, `RemoveLocalLanguageChanges`, `AddLanguageEntry` and the two settings
Activities take it as their only dependency besides language, RBAC and language folder; the tests
mock it.
A `PDOException` carries its SQLSTATE as string code, and `'42S02'` compares as a 4xx code, so
`Failure::of()` and `RendersActivityErrors` showed the database message as input error. Both now
require an integer code. The test doubles no longer call the removed `markdown()` helper.
Keeps only what the code does not say itself, as in the File component: the reasons in
`Language.php` shrink to the two real traps (no `final` Activities, the order of the contributions),
and the tests lose their history notes.
`LanguageActivity` no longer takes `Language`: only `AddLanguageEntry` needs it, for the labels of
its translation fields. The other six Activities, their `forSetup()` variants and the wiring drop
the parameter.
…st CSRF

The table actions were read from the query by `executeCommand()` without a
command, so ilCtrl never checked a token. `ilObjLanguageFolderGUI` now
implements `ilCtrlSecurityInterface`, runs the table actions only through the
unsafe command `executeTableAction` and dispatches an exact allowlist of
commands, since ilCtrl compares commands case-sensitively but PHP calls
methods case-insensitively. Takes effect after the ctrl security artifact is
rebuilt (`composer dump-autoload` or `setup build`).
…ObjLanguageFolderGUI`

Listing and downloading the deprecated entries failed with an error, because
`ilObjLanguageFolderGUI` still included `ilLangDeprecated` from the former
`Services/Language` directory.
…uageFolderGUI`

`confirmRefreshSelectedObject()`, `confirmUninstallObject()`,
`confirmUninstallChangesObject()` and the table action `edit` could no longer
be reached since the table actions confirm in modals, and their confirmations
failed for lack of language ids.
Named arguments are not used in the core (review criteria).
PHPUnit 14 no longer allows `with()` without `expects()`, so the mocks in
`SetLanguageTranslationEnabledTest`, `AddLanguageEntryTest` and
`LanguageInstallationManagerTest` now expect their calls once.
…es as `@internal`

`TranslationCache`, `InstallationRoot`, `LanguageSettings` and their implementations
are only wired in `$internal` and used inside the Language component, like the
language file directories that already carry the marker.
@c-knof
c-knof force-pushed the language-component-revision branch from 7bc37a8 to cfeb84f Compare October 23, 2026 15:38
@c-knof

c-knof commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto the current trunk (808b710a6be, LTI rework #12232). The only conflict was in Init.php and
AllModernComponents.php, where both sides append to the same lists, and both are kept. The commits from my previous comment are
now cdc2f8a (CSRF), 7beef90, 0599fd9, bec2aa8, bdec72f and cfeb84f.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement php Pull requests that update Php code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants