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)
login/login.php — DONE (2026-03-27)
login/register.php — DONE (2026-03-27)
login/forgot-password.php — DONE (2026-03-27)
login/change-password.php — modX [[!ChangePassword]] → rewrite using changePassword() in lib/auth.php
dashboard/crop-analysis/soil-test-data/soil-analysis-pdf.php — mysqli → PDO, SQL injection fix
dashboard/crop-analysis/soil-test-data/soil-report.php — display page audit
dashboard/crop-analysis/soil-test-data/soil-report-pdf.php — mysqli → PDO
dashboard/crop-analysis/plant-test-data/ — full migration
dashboard/crop-analysis/animal-dietary-balance/ — full migration
dashboard/crop-analysis/water-test-data/ — full migration
dashboard/client-settings/product-list.php — mysqli → PDO
dashboard/client-settings/updateproduct.php — SQL injection fix
dashboard/crop-cards/index.php — mysqli → PDO
dashboard/dashboard.php — replace [[$widget]] chunks with PHP components
api/updateweatherstation.php — critical SQL injection fix
api/api.php — remove deprecated mysql_connect(), use PDO
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 <head>, 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
require_once __DIR__ . '/../../config/database.php';
require_once __DIR__ . '/../../lib/auth.php';
require_once __DIR__ . '/../../lib/csrf.php';
requireLogin();
$pageTitle = 'Page Title';
$siteName = 'Crop Monitor';
include __DIR__ . '/../../layouts/header.php';
include __DIR__ . '/../../layouts/navbar.php';
include __DIR__ . '/../../layouts/sidebar.php';
?>
<!-- page content -->
<?php include __DIR__ . '/../../layouts/footer.php'; ?>
PDO query pattern
$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
<input type="hidden" name="csrf_token" value="<?= generateCsrfToken() ?>">
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
- URL compatibility: Must URL slugs matching modX resource IDs (e.g., page 41, 66) be preserved for external links?
- Credentials: Move DB password to
.env file and load with vlucas/phpdotenv or manual parse_ini_file()?
- Email / SMTP: Configure SMTP (e.g., PHPMailer + SMTP credentials) to enable password reset emails.
- Role-based access:
hasPermission() is a stub — what roles/permissions are needed?
Last updated: 2026-03-27