# CLAUDE.md

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

## What this is

PoolStat (www.poolstat.net.au): a PHP web app for running pool (billiards) leagues. It handles organisations, competitions, draws, matchsheets/livescoring, ladders, stats, comms (email/SMS) and Xero billing. It is built on **Yii 1.1.21** and runs on **PHP 7.0**. There is no build step, no test suite and no linter. Changes are checked by loading pages in the browser and reading the logs.

## Layout and environment

- `www/`: the web root. `www/index.php` is the front controller. `www/assets/` is generated by Yii (gitignored).
- `protected/`: the application (controllers, models, views, components, modules, extensions, console commands).
- `config/main.php` (web) and `config/console.php` (console) hold the active config. They contain credentials and are **gitignored**, so never commit or quote them. `config2/` and `protected/_config/` are older or alternate copies.
- The Yii framework is not in the repo. It lives one level up at `../yii-1.1.21/framework`.
- Environment selection: `www/index.php` uses `config/main.php` and `../../yii-1.1.21` unless a `dev.txt` file exists two levels above `www/`. If it exists, it switches to `protected/config/main.php`. `protected/yiic.php` does a similar PROD/DEV check for the console.
- `params['sitemode']` (`DEV`/`TEST`/`PLAY`/`PROD`) is read through `SiteIsDEV()`, `SiteIsPROD()` and the related helpers. Check its value in `config/main.php` before doing anything that sends email or SMS or calls external APIs.
- `runtime/` holds Yii logs (`application.log*`), and `protected/cache/` holds generated cache and PDF files. Both are gitignored.

## Commands

```bash
# Console commands (CConsoleCommand classes in protected/commands/*Command.php)
php protected/yiic.php <command> <action>      # e.g. php protected/yiic.php livescore activatecheck
php protected/yiic.php help                    # list commands

# Composer deps (only mpdf/qrcode at the top level; extensions have their own composer.json)
cd protected && composer install

# Logs
tail -f runtime/application.log
```

Console commands cover livescore activation, stats, housekeeping, site backup, SMS history, remote match and Browscap. They are run from cron.

## Architecture

**Routing.** `urlManager` uses path format. Most public URLs go through the custom rule `protected/components/OrgUrlRule.php`, so read that file before changing any URL. It does the following:
- It maps organisation-slug URLs (e.g. `/qebf/team-stats/1216/...`) to `organisation/*`, `public/*` and `calendar/*` routes, including the `embed` variants.
- It rewrites the API prefixes: `restapi/v1/*` → `RestapiController`, `liveapi/v1|v2/*` → `restapi` / `Apilivev2Controller`, `tmpapi/v1/*` → `Costreamv1Controller`.
- It applies the `params['siteblock']` cookie gate, which redirects to `site/restrict`. The API, live and livestream paths are exempt.
- It handles the `dashboard/home/auth/<hash>` login-key check.

**Access control.** Controllers extend `RController` from the `rights` module, which provides RBAC on top of the `user` module. They declare `filters()` returning `'rights'`. Role helpers live in `Globals.php`: `IsAdmin()`, `HasRole()` and `IsGuest()`.

**Session state.** The app keeps per-request user and guest context in session objects, not on `Yii::app()->user`:
- `SUser()` → `SessionUser::load()`
- `SGuest()` → `SessionGuest`

Both classes are in `protected/components/system/`. Org, comp and year selection usually comes from these objects.

**Global helpers.** `protected/components/Globals.php` is loaded by both configs and defines many global functions used across the codebase. Use them rather than reimplementing:
- `app()`, `user()`, `params()`
- logging and debugging: `ylog()`, `d2l()`, `p2l()`
- `SiteIsDEV()`
- path helpers: `cachefolder()`, `pdffolder()`, `scoresheetfolder()`
- `sendAPIRequest()`

**Components** (all autoloaded via `import` in config):
- `components/comp/`: competition domain logic, e.g. settings objects, `MatchsheetV2`, `CacheManager`, `PdfScoreSlips`, `StatsPlayer`.
- `components/library/`: `Lib*` static helper classes (`LibComp`, `LibOrg`, `LibPerson`, `LibAccess`, `LibApp`, `LibMSChecks`) and `MyAbly` for Ably realtime.
- `components/my/`: base and extended classes. `MyActiveRecord` adds the secondary `dbadvert` and `dbhelp` connections. There are also the configurable settings objects `MyConfig*` and `MyCSO*`, plus table and player helpers.
- `components/widgets/` (alias `widgets`): reusable view widgets such as the ladder, stats, matchsheet (`MSheet*`), results and head-to-head.
- `components/gridcolumns/`: custom `CGridView` column classes.
- `components/general/`: integrations, e.g. Mailgun, the SMS response handler, Facebook, HybridAuth and IMAP, plus import/export utilities.

**Models.** There is roughly one `CActiveRecord` per table in `protected/models/`. `Lkp*` models are lookup tables, and `Xref*` models are join tables (comp↔team, comp↔person, person↔org-year and so on). Their controllers have matching names (`XrefCompteamController` and so on), and views are in `protected/views/<controllerId>/`.

**Modules.**
- `user` and `rights`: authentication and RBAC.
- `hybridauth`: social login (gitignored).
- `btmailer`: outbound email through a Microsoft 365 sender.
- `mqtt`: livescore messaging.
- `admin`

**Extensions** (in `protected/extensions/`): YiiBooster 4.0.1 (Bootstrap UI, alias `booster`), x-editable, ecalendarview, EmailLogRoute (emails errors), MobileDetect, PHPMailgunBase, Xero, rabbitmq.

**Live scoring and APIs.** `Apilivev2Controller` is the main live-scoring API used by the mobile and scoring clients. It uses JWT tokens and is very large. `ApiBase`, `Api2Controller`, `RestapiController` and `RemoteApiController` serve the other API surfaces. `Livestream`, `Streamrig` and `Streamkey` controllers support video streaming overlays.

## Conventions and gotchas

- The tree contains many dead or backup files (`*-OLD`, `*-DEAD`, `*.php_`, `*.zip`, `*.DELETEME`). Don't edit them by mistake when searching for a class.
- Error logs go to files and are also **emailed** through `EmailLogRoute`, so noisy logging creates email.
- Timezone is set in config (`Australia/Melbourne` for web, `Australia/Sydney` for console).
