# CLAUDE.md ## Project Overview **Crop Management Platform** — modX CMS → standalone PHP migration Purpose: Centralise records for irrigation, weather, and soil moisture; supports real-time monitoring for Australian conditions. Stack: **PHP 8.4**, **MySQL**, Bootstrap 5, jQuery, Chart.js Repository root: `f:\GIT_REPO\crop_monitor` --- ## Directory Structure ``` crop_monitor/ ├── api/ # REST API endpoints (legacy) ├── client-assets/ # CSS, JS, images, uploads │ ├── css/ # Bootstrap 4 + custom styles │ ├── js/ # jQuery, Chart.js, custom scripts │ ├── table/ # DataTables AJAX helpers (legacy mysqli) │ ├── FredTemplate/ # Old template assets │ └── fullcalendar/ # Calendar widget ├── components/ # Reusable UI components (migrated) ├── config/ # Database config (PDO) ├── controllers/ # Form POST handlers (migrated) ├── dashboard/ # Main application pages │ ├── crop-analysis/ │ │ ├── soil-test-data/ # Soil analysis entry + display │ │ ├── plant-test-data/ # Plant tissue analysis │ │ ├── animal-dietary-balance/ │ │ └── water-test-data/ │ ├── client-settings/ │ ├── crop-cards/ │ ├── inbox.php │ └── planning-calendar.php ├── layouts/ # Page templates (header, footer, nav, sidebar) ├── lib/ # Utility libraries ├── login/ # Auth pages (still modX templates) ├── cropmonitor.sql # Full database schema dump ├── index.php # Empty — routing via .htaccess ├── .htaccess # Front controller rewrite rules └── .env # Empty — credentials currently in config/database.php ``` --- ## Database Schema Schema file: `cropmonitor.sql` | Table | Purpose | |-------|---------| | `client_records` | Client info + weather station API keys (`modx_user_id` FK) | | `soil_records` | Soil analysis data — 80+ columns (nutrients, base saturations, calculations) | | `soil_specifications` | Min/max nutrient ranges by soil type (used for recommendations) | | `soil_comments` | Element-specific recommendation text | | `plant_records` | Plant tissue analysis | | `plant_specifications` | Nutrient ranges by crop/growth stage | | `animal_records` | Animal dietary balance | | `animal_specifications` | Nutrient requirements by species | | `water_records` | Water quality analysis | | `weather_station` | Weather station data ingestion | | `fertiliser_specifications` | Fertilizer product database | | `block_info` | Field/block metadata | | `crop_info` | Crop definitions | | `calendar_events` | User scheduling | | `reports` | Comments/notes on analyses | | `field_sensors` | IoT sensor readings | | `sensor_id` | Sensor registration | **Important data notes:** - Most nutrient values stored as `VARCHAR(10)` — always cast to float in PHP - `modx_user_id` field links all records to users (legacy modX user system) - `rand` field on `soil_records` is a secondary verification token for URL access --- ## Migration Status ### Completed (PDO + secure) | Original modX | Migrated to | |---------------|------------| | `[[!clientDetailsFORM]]` | `components/clientDetailsForm.php` | | `[[!newClientDetails]]` | `components/newClientModal.php` + `controllers/newClientSubmit.php` | | `[[!soilformSubmit]]` | `controllers/soilTestSubmit.php` | | `[[!Personalize?]]` + `[[Wayfinder?]]` | `components/navigation.php` | | `[[$dash-header]]` + `[[$dash-footer]]` | `layouts/header.php`, `layouts/footer.php`, `layouts/navbar.php`, `layouts/sidebar.php` | | `soil-test-data.php` | Uses include-based layout system | | `soil-analysis.php` | PDO + Bootstrap 5 + calc functions | | `[[!Login]]` | `login/login.php` (session auth, CSRF, PDO) | | `[[!Register]]` | `login/register.php` (validation, auto-login, CSRF) | | `[[!ForgotPassword]]` | `login/forgot-password.php` + `login/reset-password.php` (token-based, 1hr expiry) | ### Still Needs Migration (priority order) 1. ~~**`login/login.php`**~~ — **DONE** (2026-03-27) 2. ~~**`login/register.php`**~~ — **DONE** (2026-03-27) 3. ~~**`login/forgot-password.php`**~~ — **DONE** (2026-03-27) 4. **`login/change-password.php`** — modX `[[!ChangePassword]]` → rewrite using `changePassword()` in `lib/auth.php` 5. **`dashboard/crop-analysis/soil-test-data/soil-analysis-pdf.php`** — mysqli → PDO, SQL injection fix 6. **`dashboard/crop-analysis/soil-test-data/soil-report.php`** — display page audit 7. **`dashboard/crop-analysis/soil-test-data/soil-report-pdf.php`** — mysqli → PDO 8. **`dashboard/crop-analysis/plant-test-data/`** — full migration 9. **`dashboard/crop-analysis/animal-dietary-balance/`** — full migration 10. **`dashboard/crop-analysis/water-test-data/`** — full migration 11. **`dashboard/client-settings/product-list.php`** — mysqli → PDO 12. **`dashboard/client-settings/updateproduct.php`** — SQL injection fix 13. **`dashboard/crop-cards/index.php`** — mysqli → PDO 14. **`dashboard/dashboard.php`** — replace `[[$widget]]` chunks with PHP components 15. **`api/updateweatherstation.php`** — critical SQL injection fix 16. **`api/api.php`** — remove deprecated `mysql_connect()`, use PDO 17. **`client-assets/table/gettable.php`** — AJAX DataTable endpoint, mysqli → PDO ### Legacy files to delete after migration - `post.php` (replaced by `components/clientDetailsForm.php`) - `newClientDetails.php` (replaced by `components/newClientModal.php`) - `soilAnalysisCalcs.php` (dev/debug file) - `test-analysis.php` (dev/debug file) - `dashboard/crop-analysis/soil-test-data/soil-submit.php` (replaced by `controllers/soilTestSubmit.php`) --- ## Security Audit ### Safe (PDO prepared statements) - `config/database.php` - `lib/soil_calculations.php` - `components/clientDetailsForm.php` - `controllers/newClientSubmit.php` - `controllers/soilTestSubmit.php` - `dashboard/crop-analysis/soil-test-data/soil-analysis.php` - `dashboard/crop-analysis/soil-test-data/soil-report.php` ### Critical vulnerabilities (SQL injection via unescaped params) | File | Issue | |------|-------| | `api/updateweatherstation.php` | 40+ unescaped GET params in INSERT | | `newClientDetails.php` | POST params concatenated in INSERT | | `post.php` | `$modx_user` directly in SELECT WHERE | | `soilAnalysisCalcs.php` | GET `id` directly in SELECT WHERE | | `dashboard/crop-analysis/soil-test-data/soil-submit.php` | POST params in INSERT | | `dashboard/crop-analysis/soil-test-data/soil-analysis-pdf.php` | GET params in SELECT | | `dashboard/crop-analysis/soil-test-data/soil-report-pdf.php` | GET params in SELECT | | `dashboard/crop-analysis/soil-test-data/base-saturation-pie.php` | GET params in SELECT | | `dashboard/crop-analysis/animal-dietary-balance/animal-submit.php` | POST params in INSERT | | `dashboard/crop-analysis/plant-test-data/generating-plant-analysis.php` | POST params in INSERT | | `dashboard/client-settings/updateproduct.php` | String concatenation in INSERT | | `dashboard/crop-cards/index.php` | `$client_id` in SELECT | | `client-assets/table/gettable.php` | Dynamic query from POST params | | `api/api.php` | Uses removed `mysql_connect()` (PHP 7+: fatal error) | ### Other security issues - Hardcoded credentials in multiple legacy files (`root`/`R3M0T31` and `cropmonitor`/`brvnCcaEYxlPCS3`) - `.env` file is empty — credentials should move there - `lib/auth.php` `hasPermission()` always returns `true` — RBAC not yet implemented --- ## Key Files Reference ### Config & Libraries | File | Purpose | |------|---------| | `config/database.php` | PDO connection, `getDBConnection()` function | | `lib/auth.php` | `isLoggedIn()`, `requireLogin()`, `getCurrentUserId()` | | `lib/csrf.php` | `generateCsrfToken()`, `verifyCsrfToken()`, `regenerateCsrfToken()` | | `lib/validation.php` | `sanitizeString()`, `validateNumeric()`, `ValidationException` | | `lib/soil_calculations.php` | `soilAnalysisReportCalcs()`, `soilProgramCalcs()` | | `lib/db.php` | **Empty** — intended for shared DB helpers | | `lib/flash.php` | **Empty** — intended for flash messages | ### Layouts | File | Purpose | |------|---------| | `layouts/header.php` | HTML `
`, Bootstrap 5 CDN, CSS includes. Expects `$pageTitle`, `$siteName` | | `layouts/navbar.php` | Top navigation bar | | `layouts/sidebar.php` | Left sidebar navigation | | `layouts/footer.php` | Closing tags + JS includes | ### Components | File | Purpose | |------|---------| | `components/clientDetailsForm.php` | Client dropdown + auto-fill (PDO, session user filter) | | `components/newClientModal.php` | Bootstrap modal to add client (CSRF protected) | | `components/soilAnalysisForm.php` | Full soil test entry form (~50 fields, 7 sections) | | `components/navigation.php` | Auth-aware navigation | ### Controllers | File | Purpose | |------|---------| | `controllers/newClientSubmit.php` | AJAX POST handler: creates client in `client_records` | | `controllers/soilTestSubmit.php` | POST handler: validates + inserts soil test, runs calculations, redirects to analysis | --- ## modX Tags Reference ### Replaced | Tag | Replacement | |-----|------------| | `[[!clientDetailsFORM]]` | `components/clientDetailsForm.php` | | `[[!newClientDetails]]` | `components/newClientModal.php` | | `[[!soilformSubmit]]` | `controllers/soilTestSubmit.php` | | `[[!Personalize?]]` + `[[Wayfinder?]]` | `components/navigation.php` | | `[[$dash-header]]` | `layouts/header.php` + `layouts/navbar.php` | | `[[$dash-footer]]` | `layouts/footer.php` | ### Still present in codebase (needs replacement) | Tag | Location | Replace with | |-----|----------|-------------| | `[[*longtitle]]` | `login/*.php` | PHP variable `$pageTitle` | | `[[++site_name]]` | `login/*.php` | `config/constants.php` constant | | `[[!++site_url]]` | `login/*.php` | Base URL constant | | `[[++modx_charset]]` | `login/*.php` | `'UTF-8'` | | `[[*introtext]]`, `[[*description]]` | `login/*.php` | Page-level PHP vars | | `[[!Login?...]]` | `login/login.php` | Custom PHP session login | | `[[!Register?...]]` | `login/register.php` | Custom PHP registration | | `[[!ForgotPassword?...]]` | `login/forgot-password.php` | Custom PHP password reset | | `[[!ChangePassword?...]]` | `login/change-password.php` | Custom PHP password update | | `[[!Profile]]` | `login/login.php` | Session user data | | `[[$test-widget]]`, `[[$client-widget]]`, etc. | `dashboard/dashboard.php` | PHP components | | `[[~4]]`, `[[~5]]`, `[[~10]]` | `login/*.php` | Hardcoded URL constants | --- ## Coding Patterns to Follow ### Page template pattern ```php ``` ### PDO query pattern ```php $pdo = getDBConnection(); $stmt = $pdo->prepare('SELECT * FROM soil_records WHERE id = ? AND rand = ?'); $stmt->execute([$recordId, $randId]); $row = $stmt->fetch(); ``` ### Output escaping Always use `htmlspecialchars($value, ENT_QUOTES, 'UTF-8')` when echoing user or DB data into HTML. ### CSRF in forms ```php ``` ### URL parameters used by soil analysis pages | Param | Type | Meaning | |-------|------|---------| | `cid` | int | client_records.id | | `rid` | int | soil_records.id | | `rand` | float | soil_records.rand (secondary auth token) | | `stid` | string | crop/soil type | --- ## Architecture Decisions - **No framework** — plain PHP, manual includes, no routing library - **Layout system** — `layouts/` includes (not a templating engine) - **Folder structure** — keep existing `dashboard/crop-analysis/` paths (no MVC rename needed unless requested) - **Authentication** — session-based (`$_SESSION['user_id']`); RBAC via `hasPermission()` not yet implemented - **Asset pipeline** — Bootstrap/jQuery via CDN; custom CSS/JS in `client-assets/` - **PDF generation** — `pdfchrome.php` (headless Chrome); also FPDF/mPDF patterns in some pages --- ## New auth files (2026-03-27) | File | Purpose | |------|---------| | `database/migrations/001_create_users.sql` | Run once — creates `users` + `password_resets` tables | | `lib/auth.php` | `loginUser()`, `logoutUser()`, `registerUser()`, `createPasswordResetToken()`, `validatePasswordResetToken()`, `resetPassword()`, `changePassword()` | | `login/_head.php` | Shared minimal HTML head for auth pages (Bootstrap 5, no sidebar) | | `login/_foot.php` | Closing tags + Bootstrap JS | | `login/login.php` | Email + password login with CSRF | | `login/register.php` | Full registration form with server-side validation | | `login/forgot-password.php` | Email submission for reset token | | `login/reset-password.php` | Token-validated password reset | | `login/logout.php` | Session destruction + redirect | **Note on email sending:** `forgot-password.php` generates the token and logs it via `error_log()`. No email is sent until SMTP is configured. The reset link format is `/login/reset-password.php?token={token}`. ## Open Questions 1. **URL compatibility**: Must URL slugs matching modX resource IDs (e.g., page 41, 66) be preserved for external links? 2. **Credentials**: Move DB password to `.env` file and load with `vlucas/phpdotenv` or manual `parse_ini_file()`? 3. **Email / SMTP**: Configure SMTP (e.g., PHPMailer + SMTP credentials) to enable password reset emails. 4. **Role-based access**: `hasPermission()` is a stub — what roles/permissions are needed? --- *Last updated: 2026-03-27*