Skip to content

fix: restore language setup, term writes and starter words - #333

Merged
HugoFara merged 1 commit into
developfrom
fix/first-run-term-bugs
Nov 4, 2026
Merged

HugoFara merged 1 commit into
developfrom
fix/first-run-term-bugs

Conversation

@HugoFara

@HugoFara HugoFara commented Nov 4, 2026

Copy link
Copy Markdown
Owner

Found by testing 3.8.0 on production with real data. On lwt-online.org, a new user could not set up a language properly, and nobody could change a term's status or save a term edit.

Fixes

  • New language form. The wizard wrote to the DOM without telling Alpine, so Save saw an empty form and showed a raw language.form.name_required. Retyping the name stored the language with no dictionary, language codes, parsing rules or parser. The wizard now reports every field and applies the preset's parser. It also fills in the translator URL (window.GGTRANSLATE was never assigned) and the target language when the native language is preselected. The error now uses a key that exists.
  • Term status and term edits. setWordStatus() and updateTermFull() scoped a query with $bindings, then ran it with a new literal list. That dropped the user ID, so both returned HTTP 500 in multi-user mode and worked in single-user mode.
  • Starter vocabulary. Words were inserted with WoWordCount 0 and never linked, so the reader treated them as unknown. The import now counts and links them. A migration links earlier imports, and the existing computeMissingWordCounts() fills in their counts.

The linking statement moved from LocalDictionaryService to Maintenance::linkOccurrencesToTerms() so both imports share it. It is recorded in OwnerlessTableWriteTest.

Not repaired

Languages already saved without their settings stay that way, because nothing records which settings were lost. The changelog points users to the edit page.

Tests

Each new test fails without its fix:

  • MultiUserTermWritesIntegrationTest runs as a signed-in user in multi-user mode and reproduces the production must consist of exactly 10 elements, 9 present error.
  • ScopedBindingsReachTheQueryTest is a source scan, so it also runs in CI without a database.
  • The language_wizard Vitest cases track what x-model would see.
  • 04-languages.cy.ts used to retype the name before saving, which hid the bug. It now saves what the wizard filled in, and without the fix it stays on /languages/new.

Psalm, PHPCS on the changed PHP files, PHPUnit, integration tests, Vitest and ESLint pass. Cypress passes except 24-*, the live Anki spec, which needs npm run anki:up and was not run.

The dark-theme and smaller bugs from the same test follow in a separate PR.

New users could not set up a language, and on multi-user installs nobody
could change a term's status or save a term edit.

- The language wizard wrote to the DOM without telling Alpine, so Save saw
  an empty form. Retyping the name stored the language with no dictionary,
  language codes, parsing rules or parser. The wizard now reports every
  field, applies the preset's parser, and fills in the translator URL and
  target language, which it never did.
- setWordStatus() and updateTermFull() ran a user-scoped query with a new
  literal list, dropping the user ID that scoping appends: HTTP 500 in
  multi-user mode only. A source test now rejects that shape.
- Starter words were inserted with WoWordCount 0 and never linked, so the
  reader treated them as unknown. The import counts and links them, and a
  migration repairs earlier imports.
- The e2e test retyped the wizard's name before saving, which hid the first
  bug. It now saves what the wizard filled in.
@HugoFara
HugoFara merged commit a38dd02 into develop Nov 4, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant