๐Ÿ”ง Installation & Configuration Guide โ€” PLSS Monument Field Tool

โ† Manuals โ† Back to Tool

Installation and Configuration Guide

PLSS Monument Field Tool & Survey Parcel Drawing Tool
Version 2.2 โ€” April 2026  ยท  surveyor.weist.net  ยท  JustHost/Bluehost shared hosting

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:

The main tool file was renamed from plss-field-tool.html to index.html so that surveyor.weist.net loads it directly without requiring the filename in the URL.

2. Prerequisites

Use FTP for large files. The main index.html file is approximately 400KB. The cPanel File Manager times out on files over 300KB, resulting in truncated uploads that break JavaScript parsing. Always use FileZilla or another FTP client for HTML file uploads.
MySQL version note: 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

  1. Log into cPanel
  2. Navigate to MySQL Databases
  3. Create a new database โ€” e.g. yourprefix_Surveyor
  4. Create a database user โ€” e.g. yourprefix_SurvAD
  5. 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

If the web application ever shows "Database unavailable" after you granted all privileges through cPanel, try logging out of cPanel completely, clearing browser cookies, and logging back in before opening phpMyAdmin. cPanel uses rotating session credentials that can expire.

3.3 Run the Schema

  1. Open phpMyAdmin from cPanel โ€” always launch it from inside cPanel, never from a bookmarked URL
  2. Click your database name in the left panel to select it
  3. Click the SQL tab
  4. Paste and run plss_schema.sql
  5. 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.

When future migrations are needed, temporarily re-grant those permissions, run the SQL, then remove them again.

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 typePermissionMeaning
All .php files640Owner read/write, group read, world none
All .html files644Owner read/write, group read, world read
photos/ directory750Owner read/write/execute, group read/execute, world none
PHP files should never be world-readable (644 or 755). The 640 setting ensures they execute server-side only and cannot be read as raw text.

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.

  1. Create the directory: public_html/photos/
  2. Set permissions to 750
  3. Create a .htaccess file 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

Maximum 5 photos per report, 2MB each. Client-side size check runs before upload. Server-side also enforces the limit. Supported formats: JPEG, PNG, WebP, GIF.

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.

  1. Register at iservices.dca.ca.gov
  2. Email iservices@dca.ca.gov โ€” request access to BPELSG (Board for Professional Engineers, Land Surveyors, and Geologists) license data specifically
  3. Once you receive a key, set define('DCA_API_KEY', 'your-key-here'); in config.php
  4. 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

Every delivered config.php has placeholder passwords. The lines 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.

SettingTypical value (Bluehost/JustHost)
SMTP Hostmail.yourdomain.com
SMTP Port465 (SSL) or 587 (TLS)
Usernamesurveyor@weist.net (full email address)
From NamePLSS Monument Field Tool
From Addresssurveyor@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

  1. Log into cPanel
  2. Navigate to Cron Jobs
  3. Add a new cron job with the following settings:
FieldValue
Minute0
Hour2
Day*
Month*
Weekday*
Commandphp /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.

Create the 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

8. First-Time Setup

After database setup, file upload, and config.php configuration:

  1. Navigate to surveyor.weist.net โ€” you should see the login screen
  2. Log in with username admin and password ChangeMe1
  3. You will be forced to set a new password immediately
  4. After login, click Admin tab
  5. Enter firm information in the right-hand panel โ€” firm name, address, phone, email, website
  6. Enter SMTP settings and send a test email to confirm delivery
  7. Create user accounts for your team (see Admin Guide Section 3.1)
  8. Create the photos directory and set up the cron job (Sections 4.3 and 7)

9. Migrations

9.1 Process

  1. Log into cPanel โ†’ MySQL Databases
  2. Temporarily grant ALTER, CREATE, DROP, INDEX to weistne1_SurvAD
  3. Open phpMyAdmin โ†’ click weistne1_Surveyor in left panel โ†’ SQL tab
  4. Paste and run the migration file
  5. Verify no errors
  6. Return to cPanel โ†’ Set Privileges โ†’ remove schema-change permissions
  7. Test login to confirm the web application still connects correctly
After running a migration, always test login immediately. If the migration added columns that the PHP code now expects but that don't exist yet, or if a privilege was accidentally removed, login will fail with "Database unavailable."

9.2 MySQL Compatibility Notes

9.3 Migration History โ€” surveyor.weist.net

Complete record of all migrations applied to the live database, in order:

FileDate AppliedWhat It Does
plss_schema.sqlInitial setupCreates 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.sqlApril 2026Adds must_reset_password to users; creates org_settings table; seeds SMTP settings
add-lot-support.sqlApril 2026Adds corner_mode ENUM and lot_number to search_log; adds lot_number to corner_records
add-firm-profile.sqlApril 2026Adds firm_name, firm_address, firm_phone, firm_website, firm_email to organizations; adds personal_email to users
add-reports-v2.sqlApril 2026Creates 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.sqlApril 2026Adds 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
Key lessons from migration history:
  • 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

  1. User runs Q/QQ Lookup โ†’ gets computed corner coordinates, elevation, declination
  2. User logs field search attempt in Monument Log โ†’ saved to search_log table
  3. If monument found: user creates MFR in Reports tab, linking to the log entry via search_log_id
  4. Licensed Surveyor reviews draft MFR, verifies corner designation and description, finalizes under license number
  5. If warranted: user generates Corner Record from Monument Log entry โ†’ saved to monuments table, printed and filed with county

Drawing Workflow

  1. User runs Q/QQ Lookup or enters COGO traverse
  2. Clicks Open in Drafting Tool
  3. Tool fetches user profile and firm data from get-profile.php
  4. Packages all geodetic data + firm info into localStorage handoff payload
  5. 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

12. File Reference

FilePurposeNotes
index.htmlMain PLSS field toolUpload via FTP โ€” too large for File Manager
parcel-drawing-tool.htmlSurvey Parcel Drawing Tool
survey-math.jsShared 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.htmlUser profile page
manuals.htmlDocumentation index
config.phpDatabase credentials, SMTP password, API keys, app URLAlways re-enter passwords before upload
auth.phpAuthentication logic โ€” login, token validation, permissions
login.phpLogin endpoint
logout.phpLogout endpoint
save-search.phpSave Monument Log entry
get-searches.phpRetrieve Monument Log entries โ€” requires READ_SEARCH_LOG
save-monument.phpSave monument/Corner Record to database
get-monuments.phpRetrieve monument records
save-correction.phpSave section correction offset
get-correction.phpRetrieve section correction
create-user.phpCreate new user accountAdmin only
update-user.phpUpdate user including role assignmentAdmin only
list-users.phpList organization usersAdmin only
get-audit.phpRetrieve audit logAdmin only
org-settings.phpGet/save SMTP and org settings
forgot-password.phpGenerate and send password reset email
reset-password.phpProcess password reset via token
parcel-lookup.phpAddress or APN to PLSS and parcel detailAPN input added
cors-proxy.phpProxy for NOAA CORS, NGS marks, declination, DCA verifyDCA endpoint added v2.2
api-proxy.phpProxy for Anthropic Claude AI API (COGO image extraction)
get-profile.phpReturn user profile and firm data after login
update-profile.phpSave user's own personal fields
update-org.phpSave org firm fieldsAdmin only
search-monuments.phpSearch shared community monument database
save-report.phpCreate or update Draft reportNew v2.2
get-reports.phpList reports with permission-aware filteringNew v2.2
get-report.phpGet single report with photosNew v2.2
finalize-report.phpChange Draft to Final โ€” requires verified licenseNew v2.2
recall-report.phpRecall a report โ€” admin onlyNew v2.2
upload-report-photo.phpUpload photo to report โ€” 2MB max, 5 per reportNew v2.2
cron-reports.phpNightly: flip old Drafts public, flag superseded DPRsNew v2.2 โ€” run via cron

13. Troubleshooting

ProblemCheck / Fix
"Database unavailable" on login1) 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 definedindex.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 tabUser 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 tabPHP file using $db_host variables instead of get_db() โ€” re-upload fixed versions of all reports PHP files.
Monument Log empty for userUser'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 uncheckOld index.html cached โ€” hard refresh (Cmd+Shift+R / Ctrl+Shift+R) or open incognito window.
"Log Search Attempt" modal visible at bottom of pageindex.html is missing the overlay div wrapper โ€” re-upload current version which includes the fix.
Password reset email not receivedSMTP settings in Admin Console; check spam folder; run Full SMTP Diagnostic; verify SMTP_PASSWORD in config.php is correct.
CORS stations not appearing on mapNormal โ€” NOAA API is intermittently slow. Stations appear when available.
#1215 Cannot add foreign key on migrationRun 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 drawingConfirm add-firm-profile.sql was applied. Test get-profile.php directly in browser to confirm it returns firm data.
Report finalization blockedDCA_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 errorsConfirm /photos/ directory exists, is permission 750, and has .htaccess with "Deny from all". Confirm web server user has write access.
Cron job not runningVerify 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.