# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What this is

The biceps.ch website (French-language e-learning: spelling/dictation, conjugation, vocabulary, maths, videos) running on **TYPO3 4.5.40** — a legacy, pre-namespace, pre-Composer TYPO3. `src/` is the web root and contains the full installation including the core (`t3lib/`, `typo3/`), which has been locally patched over the years.

There is no README and no build system. The project root is a **local-only git repository** (no remote) used to track changes; it contains credentials (`src/typo3conf/localconf.php`, `src/postfinance/index.php`, the Behat feature), so it must not be pushed anywhere as is. `.gitignore` excludes runtime caches, `src/postfinance/reports/`, large media files and `tests/libraries/`.

Database content (pages, TypoScript template records, plugin flexforms, skin records) is not in this tree. Much of the wiring lives there: per-page TypoScript constants in `sys_template`, and the `tx_xcibskins_skins` records that decide which skin files are loaded. When a question depends on it, ask the user for an export rather than guessing.

## Deployment

`.idea/deployment.xml` maps `src/` to the production host (`/www2.biceps.ch` at SwissCenter). Automatic upload is disabled: the user uploads changed files manually from PhpStorm, so list the files to deploy when a change is finished. There is no staging environment — nothing can be run through TYPO3 locally, and changes are only exercised for real once uploaded. If `autoUpload` reappears in `deployment.xml`, every save under `src/` goes straight to production; check before editing.

After uploading a change to an `ext_localconf.php` or `ext_tables.php`, all caches must be cleared in the TYPO3 backend (they are concatenated into `typo3conf/temp_CACHED_*`).

Paths excluded from upload (live only on the server, absent locally): `uploads/`, `typo3temp/`, `typo3conf/l10n/`, `fileadmin/{donnees,images,jobs,js,user_upload}`, `postfinance/reports`.

## Commands

No build, no linter config, no unit test suite for the site code.

- **Syntax check a PHP file**: `/usr/bin/php5.6 -l <file>` — the project language level is PHP 5.6 and code must stay compatible with it (no scalar type hints, no `??`, no return types; PHP4-style constructors, `var` and `?>` closing tags are common in the custom extensions). The default `php` CLI is far newer and is not a valid check. `-l` only catches syntax errors; to exercise a class without TYPO3, run it under `php5.6` in the scratchpad with a small stand-in for the `t3lib_div` methods it uses.
- **SCSS**: compiled per file, next to the source, with `scss --no-cache --update file.scss:file.css` (PhpStorm file watcher, currently disabled). Applies to `src/postfinance/*.scss` and `biceps_admin_oauth2/Resources/Public/Css/`. Skin `.scss` files under `fileadmin/sites/*/skins/*/css/` can be compiled at runtime by `xcib_skins` (bundled PHamlP, `tx_xcibskins_div`); some skin folders also hold a compiled `.css` next to the `.scss`, and which file a skin actually loads is set in its database record — ask before regenerating or deleting either.
- **Browser tests** (Behat + Mink + Selenium, in `tests/`, independent from `src/`):
  - Install: `cd tests && composer install` (vendor dir is `tests/libraries`, bins in `tests/bin`)
  - Run all: `tests/bin/execute tests/tests`
  - Run one feature: `tests/bin/execute tests/tests/Features/dictee/20171204-dictee-correction.feature`
  - Options: `--browser=firefox|chrome`, `--browser-driver=<path>` (default `/opt/webdriver/geckodriver`), `--selenium-port=<n>` (default 8889), `--behat-options="..."`
  - `execute` starts the Selenium standalone server itself (needs `java`) and kills it afterwards. Features navigate to absolute URLs on the **live site** and log in with a real test account — they are not run against a local instance.
  - Step definitions come from the `cdsrc/selenium-behat` dictionaries (`tests/libraries/cdsrc/selenium-behat`, its own git checkout); `FeatureContext` only composes those traits.

## Architecture

### Configuration

- `src/typo3conf/localconf.php` — TYPO3 4.x style global config (DB credentials, `extList` / `extList_FE`, extension settings as serialized `extConf`). An extension only loads if listed in **both** lists as appropriate; the file contains several historical `extList` assignments, the last one wins.
- `src/typo3conf/realurl_conf.php` + `typo3conf/realurl/{_DEFAULT,site}.php` — RealURL config is built dynamically per domain/language and cached compressed in `typo3temp/.ht_realurlcache` for 1 hour.
- The DB connection is forced to `SET NAMES latin1` while the backend uses utf-8; many source files of the `xcib_*` extensions are themselves ISO-8859-1 encoded. Check a file's encoding before editing and preserve it.
- `src/.htaccess` carries functional routing: `/svideo/*` → `xcib_flowplayer/lib/extcall/index.php`, `/jobs/*.pdf` → `fileadmin/jobs/`, versioned-filename stripping (`name.123.css` → `name.css`), and denies for `typo3temp/xcib_biceps/`, log files and `Resources/Private`.

### Custom extensions (`src/typo3conf/ext/`)

Custom code lives in the `xcib_*`, `cdsrc_*` and `biceps_*` extensions (all by the same author); everything else there is third-party (templavoila, realurl, tt_news, direct_mail, pw_comments, scriptmerger, …). Classic pibase layout: `piN/` frontend plugins, `modN/` backend modules, `tca/`, `hooks/`, `lib/`, `scheduler/`, `ext_tables.sql`, `locallang*.xml`.

Class hierarchy that most frontend work goes through:

```
tslib_pibase
└─ tx_xciblib_pi            xcib_lib/pi/          shared plugin base
   └─ tx_xcibbiceps_pi      xcib_biceps/pi/       generic exercise engine
      ├─ tx_xcibbicepsspell_pi1   xcib_biceps_spell   spelling / dictation (TTS)
      ├─ tx_xcibbicepsconj_pi1    xcib_biceps_conj    conjugation
      ├─ tx_xcibbicepsvoc_pi1     xcib_biceps_voc     vocabulary
      └─ tx_xcibbicepssnoopy_pi1  xcib_biceps_snoopy
```

- **`xcib_lib`** is the foundation every other `xcib_*` extension depends on. `tx_xciblib_pi::main()` initialises language, flexform, piVars (optionally persisted in cookies) and storage folders, then **dispatches to a method named by the `CMD` value** (from TypoScript `conf['CMD']`, else the plugin flexform), falling back to the `$defaultCMD` argument. Adding a plugin view means adding a method and exposing it as a CMD option in the flexform. Templates are marker-based `.tmpl` files loaded via `loadTemplateView()`; `substituteAll()` also resolves language-label and TypoScript markers. `tx_xciblib_div` holds the shared static helpers.
- **`xcib_biceps`** implements the exercise lifecycle once — folders/lists of items, building a "query" (an exercise session), answering, statistics, printable PDF, list editing — as overridable `render*` / `getQuery*` / `checkQueryAnswer` methods. Subject extensions override those rather than reimplementing the flow.
- **AJAX** goes through TYPO3 `eID` scripts (`index.php?eID=xcib_biceps`, `xcib_biceps_spell`, `cdsrc_biceps_comments`). `Tx_Xcib_Biceps_AbstractAjaxRequestHandler` bootstraps a minimal TSFE and caches the serialized TSFE/template objects in `typo3temp/xcib_biceps/` per page+user; the `eType` parameter (`list`, `query`, `search`) selects the handler class. Stale behaviour after TypoScript changes can come from that cache.
- **Site search** (`eType=search`, `Tx_Xcib_Biceps_SearchHandler`) is driven entirely by `plugin.tx_xcibbiceps.search.*`, defined per subject in `skins/<skin>/ts/setup/user.search.<subject>.txt` and duplicated in `v2_base` (desktop) and `v2_tablet` with different page ids — change both. Sub-keys are separate sources: `lists`, `quiz`, `quizsvg`, `custom` and `documents` query local tables; `json` searches the remote catalogues. A `json` entry gives `type` (`video`, `paper` or `annexe`, mapped to the remote item type in `$jsonMatchingTypes`), `categories`, `servers`, `pid` of the listing page and `branch`. Nothing hides the search box when a page has no search config.
- **Remote media catalogues**: videos are listed by `v1`–`v3.biceps.ch`, documents and annexes by `d1`–`d3.biceps.ch`, at `<server>/list/<category ids>.json` (a tree of `category` / `video` / `document` items). The server lists are TypoScript constants (`biceps.branch.matter.listing.servers` and `.servers.documents`); the category of each listing page is a constant on that page's `sys_template` record. The servers are not guaranteed to return identical data. All fetching goes through `tx_xciblib_remotelisting::fetch()` (`xcib_lib/lib/`), which caches one file per listing in `typo3temp/xcib_lib_listing/`, shared by the search (accepts a copy up to 60 min old) and the `xcib_videos` / `xcib_documents` listing renderers (5 min), serves the expired copy when no server answers, and is emptied by the backend "clear all caches" / "clear page cache" actions.
- **`xcib_biceps_spell`** text-to-speech is pluggable under `lib/classes/Services/` (`Biceps`, `Voxygen`, `Voxygen_NTTS`), selected through the extension configuration (`tts.default.service`). `tests/voxygen/` holds standalone CLI scripts for trying the Voxygen API.
- **`xcib_skins`** replaces normal TypoScript template handling: it XCLASSes `t3lib_tstemplate` / `t3lib_tsparser_ext` (and TemplaVoila classes) and generates TypoScript, CSS/JS includes and TemplaVoila template objects from **skin folders** resolved along the page rootline. A skin is a directory with `ts/{constants,setup,tsconfig}/*.txt`, `css/`, `js/`, `img/`, `tv/to/{page,fce}`. A file in a skin folder is only loaded if the skin's database record lists it (`setup_files`, `constants_files`, `css_files`, `js_files`), and the record's own `setup` / `constants` fields are appended after the files — so a file that nothing references by name is not necessarily unused, and an object that the files define and then clear (e.g. `lib.infos >` in `v2_base`) is probably redefined in the database. Skins live in `src/fileadmin/sites/<site>/skins/<skin>/` for the three sites `principal`, `ecole_obligatoire`, `ecole_superieure`. Most site TypoScript and styling therefore lives in `fileadmin/sites/`, not in extensions or the DB.
- **`xcib_feusers`** handles frontend users, licences, invoicing/payment and a licence-check scheduler task; **`xcib_payments`** / `src/postfinance/` (standalone camt.054 report viewer, own DB connection, not routed through TYPO3) cover payments.
- **`biceps_admin_oauth2`** is the one Extbase-style extension (`Classes/`, `Resources/`). It registers an `auth` service so frontend login is delegated via OAuth2 to the separate Biceps administration application, and ships its own Composer `vendor/` (league/oauth2-client) loaded through its `autoload.php`.

### Templates

Plugin marker templates are in each extension's `pi*/tmpl/`, with site-level overrides in `src/fileadmin/templates/<extkey>/` (e.g. `fileadmin/templates/xcib_biceps/query_spelling_dictate.tmpl`) and `src/fileadmin/sites/<site>/templates/`. Check for an override in `fileadmin` before editing an extension's own template.

### Other top-level directories

- `src/tests/` — ad-hoc manual test pages served from the web root (audio, quizsvg, videos), not automated tests.
- `tools/videosexport/` — one-off CLI script (`php tofolder.php`) reorganising exported videos into folders from `videos.json` / `links.json`.
- `doc/`, `result/` — empty.

## Conventions

- Class names follow TYPO3 4 conventions: `tx_<extkeywithoutunderscores>_<suffix>` in `class.<name>.php`; newer helper classes use `Tx_Xcib_Biceps_*` and must be registered in the extension's `ext_autoload.php` (lower-cased class name → path) or `require_once`d explicitly — there is no PSR autoloading outside `biceps_admin_oauth2`.
- Use the TYPO3 4.5 API (`t3lib_div`, `t3lib_extMgm`, `$GLOBALS['TYPO3_DB']->exec_*query`, `$GLOBALS['TSFE']`); nothing from TYPO3 6+ (namespaces, `GeneralUtility`, Doctrine DBAL) exists here.
- Schema changes go in the extension's `ext_tables.sql` and are applied through the Extension Manager / Install Tool on the server.
