Installation and Configuration Guide
1. Overview
This guide covers deploying the PLSS Monument Field Tool and Survey Parcel Drawing Tool to a shared hosting environment for the first time, or applying updates to an existing installation. It also documents every migration applied to the live database at surveyor.weist.net as a permanent record.
The system consists of:
index.htmlโ Main PLSS field tool (formerly plss-field-tool.html)parcel-drawing-tool.htmlโ Survey Parcel Drawing Toolsurvey-math.jsโ Shared geodetic math library (GRS80 constants, LCC/TM projections, SP_ZONES). Required by bothindex.htmlandparcel-drawing-tool.html. Must be deployed alongside both files.profile.htmlโ User profile pagemanuals.htmland five manual/guide HTML files- Approximately 30 PHP API files
- A MySQL database (
weistne1_Surveyor) - A
/photos/directory for report photo uploads
plss-field-tool.html to index.html so that surveyor.weist.net loads it directly without requiring the filename in the URL.2. Prerequisites
- FTP client (FileZilla recommended) or cPanel File Manager access
- phpMyAdmin access via cPanel
- PHP 8.0 or higher (tool currently runs on 8.2.30)
- MySQL 5.7 or higher
- A working email account for outgoing password reset and notification emails
ALTER TABLE ... ADD COLUMN IF NOT EXISTS requires MySQL 8.0.3 or later and is NOT supported on most shared hosting including Bluehost. All migration files in this project use separate ALTER statements without IF NOT EXISTS for compatibility.3. Database Setup
3.1 Create the Database
- Log into cPanel
- Navigate to MySQL Databases
- Create a new database โ e.g.
yourprefix_Surveyor - Create a database user โ e.g.
yourprefix_SurvAD - Set a strong password and record it securely โ you will need it for config.php
3.2 Assign Permissions
Under Add User to Database in cPanel MySQL Databases:
Installation-only โ remove after setup: ALTER, ALTER ROUTINE, CREATE, CREATE ROUTINE, DROP, EVENT, INDEX, TRIGGER
Permanent running-state โ keep always: SELECT, INSERT, UPDATE, DELETE, EXECUTE, SHOW VIEW, LOCK TABLES, REFERENCES
3.3 Run the Schema
- Open phpMyAdmin from cPanel โ always launch it from inside cPanel, never from a bookmarked URL
- Click your database name in the left panel to select it
- Click the SQL tab
- Paste and run
plss_schema.sql - Verify no errors โ you should see 20+ tables created
3.4 Seed Initial Data
After the schema runs, insert your organization and first admin user. Replace all placeholder values:
-- Organization record INSERT INTO organizations ( org_name, org_short, is_active, tier, firm_name, firm_address, firm_phone, firm_website, firm_email, created_at ) VALUES ( 'Your Firm Name', 'YOURCODE', 1, 'professional', 'Your Firm Name', 'Your Address, City, State ZIP', 'Your Phone Number', 'yourwebsite.com', 'info@yourfirm.com', NOW() ); -- First admin user -- Password hash below = 'ChangeMe1' -- User is forced to change on first login INSERT INTO users ( org_id, username, email, personal_email, display_name, password_hash, license_type, license_number, license_state, must_reset_password, is_active, is_email_verified, failed_login_count, created_at ) VALUES ( 1, 'admin', 'admin@yourfirm.com', 'admin@yourfirm.com', 'Your Name', '$2y$12$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.usu5NW54i', 'PLS', 'your_license_number', 'CA', 1, 1, 1, 0, NOW() ); -- Assign System Architect role INSERT INTO user_roles ( user_id, role_id, granted_by, granted_at, granted_reason, is_active ) VALUES (1, 1, 1, NOW(), 'Initial setup', 1);
3.5 Remove Installation Permissions
After the schema and seed data run successfully, return to cPanel MySQL Databases and remove: ALTER, ALTER ROUTINE, CREATE, CREATE ROUTINE, DROP, EVENT, INDEX, TRIGGER.
4. File Upload
4.1 Directory Structure
All files go into the document root for your domain. Use FTP (FileZilla) for files over 300KB โ never the cPanel File Manager for large files.
index.html โ main tool (formerly plss-field-tool.html) parcel-drawing-tool.html profile.html manuals.html user-guide.html admin-guide.html install-config-guide.html howto-guide.html sql-reference-guide.html โ new v2.2 config.php auth.php login.php logout.php save-search.php get-searches.php save-monument.php get-monuments.php save-correction.php get-correction.php create-user.php update-user.php list-users.php get-audit.php org-settings.php forgot-password.php reset-password.php parcel-lookup.php cors-proxy.php โ includes DCA verify endpoint api-proxy.php get-profile.php update-profile.php update-org.php search-monuments.php save-report.php โ new v2.2 โ Reports system get-reports.php โ new v2.2 get-report.php โ new v2.2 finalize-report.php โ new v2.2 recall-report.php โ new v2.2 upload-report-photo.php โ new v2.2 cron-reports.php โ new v2.2 โ runs via cPanel cron photos/ โ new v2.2 โ report photo storage (see 4.3)
4.2 File Permissions
| File type | Permission | Meaning |
|---|---|---|
All .php files | 640 | Owner read/write, group read, world none |
All .html files | 644 | Owner read/write, group read, world read |
photos/ directory | 750 | Owner read/write/execute, group read/execute, world none |
4.3 Photos Directory
The Reports system stores uploaded field photos in a /photos/ directory on the server. This directory must exist and must NOT be directly web-accessible.
- Create the directory:
public_html/photos/ - Set permissions to 750
- Create a
.htaccessfile inside/photos/containing:
Deny from all
This prevents anyone from accessing photo files directly via URL. Photos are served through PHP endpoints that enforce authentication โ not directly from the web server.
Photos are organized as: /photos/{org_id}/{report_id}/photo_N.jpg
5. Configuration
5.1 Edit config.php
Edit config.php before uploading โ fill in all values marked with your actual credentials:
// โโ DATABASE โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ define('DB_HOST', 'localhost'); define('DB_NAME', 'yourprefix_Surveyor'); define('DB_USER', 'yourprefix_SurvAD'); define('DB_PASS', 'YOUR_ACTUAL_DB_PASSWORD'); โ must be set define('DB_CHARSET', 'utf8mb4'); // โโ APPLICATION โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ define('APP_NAME', 'PLSS Monument Field Tool'); define('APP_VERSION', '2.2.0'); define('APP_URL', 'https://surveyor.weist.net'); โ your domain // โโ SMTP โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ define('SMTP_PASSWORD', 'YOUR_ACTUAL_SMTP_PASSWORD'); โ must be set // โโ DCA LICENSE VERIFICATION โโโโโโโโโโโโโโโโโโโโโโโโโโ define('DCA_API_KEY', ''); โ leave empty until key obtained // โโ PRODUCTION SETTINGS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ define('DEBUG_MODE', false); โ always false in production define('PDO::ATTR_PERSISTENT', false); โ keep false on shared hosting
5.2 DCA API Key (License Verification)
The DCA API key enables real-time California professional license verification for report finalization. Without it, finalization is blocked.
- Register at iservices.dca.ca.gov
- Email iservices@dca.ca.gov โ request access to BPELSG (Board for Professional Engineers, Land Surveyors, and Geologists) license data specifically
- Once you receive a key, set
define('DCA_API_KEY', 'your-key-here');in config.php - Upload config.php (with passwords re-entered)
Until the key is configured, the system returns API_NOT_CONFIGURED and displays a message directing users to contact the administrator.
5.3 The config.php Password Rule
define('DB_PASS', 'YOUR_PASSWORD_HERE'); and define('SMTP_PASSWORD', 'YOUR_SMTP_PASSWORD_HERE'); are placeholders โ never real values. You must manually replace them with your actual passwords before every upload. Forgetting this causes "Database unavailable" errors for all users. Keep the previous config.php until the new version is confirmed working โ you will need the passwords from it.6. SMTP Email
SMTP settings are configured through the Admin Console after login. The tool uses direct SMTP โ not PHP's mail() function โ for reliability on shared hosting.
| Setting | Typical value (Bluehost/JustHost) |
|---|---|
| SMTP Host | mail.yourdomain.com |
| SMTP Port | 465 (SSL) or 587 (TLS) |
| Username | surveyor@weist.net (full email address) |
| From Name | PLSS Monument Field Tool |
| From Address | surveyor@weist.net |
The SMTP password is set in config.php โ not in the database. After saving SMTP settings in the Admin Console, use Send Test Email to confirm delivery, then Full SMTP Diagnostic if it fails.
7. Cron Job Setup
A nightly cron job is required to flip Draft reports to public visibility after 180 days and to flag DPR reports when newer field data is logged for the same section.
Setting Up in cPanel
- Log into cPanel
- Navigate to Cron Jobs
- Add a new cron job with the following settings:
| Field | Value |
|---|---|
| Minute | 0 |
| Hour | 2 |
| Day | * |
| Month | * |
| Weekday | * |
| Command | php /home/weistne1/public_html/cron-reports.php >> /home/weistne1/logs/cron-reports.log 2>&1 |
This runs cron-reports.php at 2:00 AM daily. Output is appended to a log file. Adjust the path to match your actual home directory.
logs/ directory in your home directory before setting up the cron job, or the log redirect will fail silently. The cron job will still run โ it just won't log.What the Cron Job Does
- Finds Draft reports where
DATEDIFF(NOW(), created_at) >= 180andreport_status = 'DRAFT'โ setsvisible_public = 1 - Finds DPR reports for sections that now have a section correction logged โ sets a flag in
report_bodyJSON - Recalled reports are never made public โ the WHERE clause requires DRAFT status
8. First-Time Setup
After database setup, file upload, and config.php configuration:
- Navigate to surveyor.weist.net โ you should see the login screen
- Log in with username
adminand passwordChangeMe1 - You will be forced to set a new password immediately
- After login, click Admin tab
- Enter firm information in the right-hand panel โ firm name, address, phone, email, website
- Enter SMTP settings and send a test email to confirm delivery
- Create user accounts for your team (see Admin Guide Section 3.1)
- Create the photos directory and set up the cron job (Sections 4.3 and 7)
9. Migrations
9.1 Process
- Log into cPanel โ MySQL Databases
- Temporarily grant ALTER, CREATE, DROP, INDEX to
weistne1_SurvAD - Open phpMyAdmin โ click
weistne1_Surveyorin left panel โ SQL tab - Paste and run the migration file
- Verify no errors
- Return to cPanel โ Set Privileges โ remove schema-change permissions
- Test login to confirm the web application still connects correctly
9.2 MySQL Compatibility Notes
- Do not use
IF NOT EXISTSin ALTER TABLE statements โ not supported on shared hosting MySQL - If an ALTER fails because the column already exists, skip that statement and continue
- All foreign key column types must match exactly โ use
INT(10) UNSIGNEDthroughout to match the existing schema - If
#1215 โ Cannot add foreign key constraintappears, check that referenced table primary key names and types match exactly usingSHOW CREATE TABLE tablename; - Use
INSERT IGNOREfor permission and role seeding so re-running is safe
9.3 Migration History โ surveyor.weist.net
Complete record of all migrations applied to the live database, in order:
| File | Date Applied | What It Does |
|---|---|---|
| plss_schema.sql | Initial setup | Creates all base tables: organizations, users, roles, permissions, role_permissions, user_roles, user_permissions, sessions, auth_providers, email_notifications, projects, monuments, monument_observations, corner_records, search_log, cogo_traverses, photos, dual_control_requests, audit_log, section_corrections, org_settings |
| add-password-reset.sql | April 2026 | Adds must_reset_password to users; creates org_settings table; seeds SMTP settings |
| add-lot-support.sql | April 2026 | Adds corner_mode ENUM and lot_number to search_log; adds lot_number to corner_records |
| add-firm-profile.sql | April 2026 | Adds firm_name, firm_address, firm_phone, firm_website, firm_email to organizations; adds personal_email to users |
| add-reports-v2.sql | April 2026 | Creates reports table (with full FK constraints matched to actual schema); creates report_photos table; adds license_verified and license_verified_date to users; seeds 8 report permission codes; seeds 4 new roles (Licensed Surveyor, Org Admin, Read-Only Viewer, Auditor); seeds Survey Technician, Field Technician, Guest roles; assigns all report permissions to all roles |
| add-delete-search-log.sql | April 2026 | Adds DELETE_SEARCH_LOG_OWN permission; assigns full non-report permission matrix to all roles (READ_SEARCH_LOG, QUERY, CREATE_MONUMENT, EDIT_OWN_MONUMENT, EDIT_ANY_MONUMENT, DELETE_MONUMENT, READ_AUDIT_LOG, EXPORT_DATA, GENERATE_CR_DRAFT, GENERATE_CR_FINAL, FILE_CR); adds MANAGE_USERS to Org Admin |
- The permissions table requires perm_group, perm_name, perm_description, requires_dual_ctrl, is_destructive, is_active, created_at โ INSERT without all of these will fail
- The roles table requires org_id, is_system_role, is_active, created_by, created_at
- The role_permissions table requires granted_by and granted_at โ not just role_id and perm_id
- license_type already existed in users as ENUM before add-reports-v2.sql โ do not add it again
- organizations.org_short VARCHAR(30) already exists โ use it for report filenames, no new column needed
10. How Data Flows
Field Workflow
- User runs Q/QQ Lookup โ gets computed corner coordinates, elevation, declination
- User logs field search attempt in Monument Log โ saved to
search_logtable - If monument found: user creates MFR in Reports tab, linking to the log entry via
search_log_id - Licensed Surveyor reviews draft MFR, verifies corner designation and description, finalizes under license number
- If warranted: user generates Corner Record from Monument Log entry โ saved to
monumentstable, printed and filed with county
Drawing Workflow
- User runs Q/QQ Lookup or enters COGO traverse
- Clicks Open in Drafting Tool
- Tool fetches user profile and firm data from
get-profile.php - Packages all geodetic data + firm info into localStorage handoff payload
- Drafting Tool opens, reads payload, pre-populates everything including firm title block
Report Filename Convention
MtDiablo-04N-06E-Sec14-NWSW-MFR-2026-04-16-WEI-RW-Draft
{Meridian}-{TWP}-{RNG}-{Section}-{Corner}-{Type}-{Date}-{OrgCode}-{Initials}-{Status}
Meridian names: MtDiablo, Humboldt, SanBern, SaltLake, NewMexico, 6thPrincipal, GilaSalt, Navajo, Willamette. Corner codes: NWSW, SENE, LT04 (lot corners), CNTR, NQ00, SQ00, EQ00, WQ00, XXXX (not applicable). Status: Draft, Final (Recalled not in filename). OrgCode comes from organizations.org_short.
11. Security Checklist
- config.php has correct database credentials (DB_PASS is not a placeholder)
- config.php has SMTP_PASSWORD set to actual email password
- DEBUG_MODE is set to false in config.php
- PDO persistent connections are false in config.php
- All .php files are permission 640
- All .html files are permission 644
- photos/ directory exists, is permission 750, and has Deny from all in .htaccess
- Schema-change database permissions removed after initial setup and after each migration
- First admin password changed from default ChangeMe1
- APP_URL in config.php matches actual domain exactly
- Firm information entered via Admin Console
- SMTP test email received successfully
- Cron job set up and confirmed running in cPanel Cron Jobs
- DCA_API_KEY set in config.php (or documented as pending)
- All new PHP files use get_db() โ not new PDO() with $db_host variables
- All new PHP files use Auth::require_auth() โ not Auth::require_login() which does not exist
- All JavaScript fetch calls include Authorization: Bearer token header
12. File Reference
| File | Purpose | Notes |
|---|---|---|
| index.html | Main PLSS field tool | Upload via FTP โ too large for File Manager |
| parcel-drawing-tool.html | Survey Parcel Drawing Tool | |
| survey-math.js | Shared geodetic math โ LCC/TM projections, SP_ZONES, GRS80 constants. Loaded by both index.html and parcel-drawing-tool.html before their own scripts. Must be in the same directory as both HTML files. | |
| profile.html | User profile page | |
| manuals.html | Documentation index | |
| config.php | Database credentials, SMTP password, API keys, app URL | Always re-enter passwords before upload |
| auth.php | Authentication logic โ login, token validation, permissions | |
| login.php | Login endpoint | |
| logout.php | Logout endpoint | |
| save-search.php | Save Monument Log entry | |
| get-searches.php | Retrieve Monument Log entries โ requires READ_SEARCH_LOG | |
| save-monument.php | Save monument/Corner Record to database | |
| get-monuments.php | Retrieve monument records | |
| save-correction.php | Save section correction offset | |
| get-correction.php | Retrieve section correction | |
| create-user.php | Create new user account | Admin only |
| update-user.php | Update user including role assignment | Admin only |
| list-users.php | List organization users | Admin only |
| get-audit.php | Retrieve audit log | Admin only |
| org-settings.php | Get/save SMTP and org settings | |
| forgot-password.php | Generate and send password reset email | |
| reset-password.php | Process password reset via token | |
| parcel-lookup.php | Address or APN to PLSS and parcel detail | APN input added |
| cors-proxy.php | Proxy for NOAA CORS, NGS marks, declination, DCA verify | DCA endpoint added v2.2 |
| api-proxy.php | Proxy for Anthropic Claude AI API (COGO image extraction) | |
| get-profile.php | Return user profile and firm data after login | |
| update-profile.php | Save user's own personal fields | |
| update-org.php | Save org firm fields | Admin only |
| search-monuments.php | Search shared community monument database | |
| save-report.php | Create or update Draft report | New v2.2 |
| get-reports.php | List reports with permission-aware filtering | New v2.2 |
| get-report.php | Get single report with photos | New v2.2 |
| finalize-report.php | Change Draft to Final โ requires verified license | New v2.2 |
| recall-report.php | Recall a report โ admin only | New v2.2 |
| upload-report-photo.php | Upload photo to report โ 2MB max, 5 per report | New v2.2 |
| cron-reports.php | Nightly: flip old Drafts public, flag superseded DPRs | New v2.2 โ run via cron |
13. Troubleshooting
| Problem | Check / Fix |
|---|---|
| "Database unavailable" on login | 1) DB_PASS in config.php is a placeholder โ re-enter real password and re-upload. 2) cPanel phpMyAdmin session expired โ log out of cPanel, clear cookies, log back in. 3) Run SHOW PROCESSLIST in phpMyAdmin to check for stuck queries. |
| Login button does nothing / doLogin not defined | index.html was truncated during upload โ use FTP to re-upload. Check file size: should be ~400KB. Verify with View Source that "function doLogin" appears. |
| "Authentication required" on Errata tab | User has no role assigned โ check user_roles table. Also verify all new PHP files use Auth::require_auth() not Auth::require_login(). |
| "Unexpected end of JSON input" on Errata tab | PHP file using $db_host variables instead of get_db() โ re-upload fixed versions of all reports PHP files. |
| Monument Log empty for user | User's role missing READ_SEARCH_LOG permission โ run the add-delete-search-log.sql migration which assigns it to all appropriate roles. |
| Equipment checkboxes won't uncheck | Old index.html cached โ hard refresh (Cmd+Shift+R / Ctrl+Shift+R) or open incognito window. |
| "Log Search Attempt" modal visible at bottom of page | index.html is missing the overlay div wrapper โ re-upload current version which includes the fix. |
| Password reset email not received | SMTP settings in Admin Console; check spam folder; run Full SMTP Diagnostic; verify SMTP_PASSWORD in config.php is correct. |
| CORS stations not appearing on map | Normal โ NOAA API is intermittently slow. Stations appear when available. |
| #1215 Cannot add foreign key on migration | Run SHOW CREATE TABLE on referenced tables to verify exact column names and types. All FK columns must be INT(10) UNSIGNED to match the schema. |
| Firm block empty in drawing | Confirm add-firm-profile.sql was applied. Test get-profile.php directly in browser to confirm it returns firm data. |
| Report finalization blocked | DCA_API_KEY not set in config.php โ either set it up (see Section 5.2) or license_verified not set for user in users table. |
| Photos directory errors | Confirm /photos/ directory exists, is permission 750, and has .htaccess with "Deny from all". Confirm web server user has write access. |
| Cron job not running | Verify path in cPanel Cron Jobs matches actual file location. Check /home/username/logs/ for cron-reports.log output. Test manually: run the PHP file directly in browser to see any errors. |