A community-driven web application for reporting, tracking, and moderating traffic-related accidents and near misses in Edmonton.
Built with PHP + MySQL backend and a vanilla JS + Leaflet frontend.
- OAuth login via Google, Apple, Mastodon, BlueSky
- Random alias generator (adjective + adjective + noun) for privacy
- User roles: user, moderator, admin
- Privacy settings: public / logged-in only / private
- Ban enforcement (login blocked for banned users)
- User profile pages with reports and comments
- Report Accidents or Near Misses
- Categories: Pedestrian, Cyclist, Motor Vehicle
- Severity levels: Minor, Moderate, Severe
- Upload photos (stored in S3)
- Status workflow: Pending β Approved/Rejected
- Reports displayed on OpenStreetMap with clustering
- Users can comment on reports
- Flag system for inappropriate reports/comments
- Moderators can dismiss or remove flagged content
- Unified moderation dashboard:
- Pending reports (approve/reject with notes)
- Flagged content (dismiss/remove with notes)
- Settings (require approval before publishing)
- Analytics
- Moderation log
- Moderation log:
- Logs all actions (approve/reject, ban/unban, flag resolve)
- Immutable moderation notes
- Filters: by action, keyword, date range
- Total reports (approved, rejected, pending)
- Approval vs rejection trends (weekly chart)
- Average moderation resolution time
- Moderator performance insights (extendable)
- Backend: PHP 8+, MySQL 8, PDO
- Frontend: HTML5, CSS3, JavaScript (ES6 Modules)
- Maps: Leaflet.js + MarkerCluster
- Auth: OAuth 2.0 (Google, Apple, Mastodon, BlueSky)
- Storage: Amazon S3 (photo uploads)
accident-reports/
βββ api/
β βββ auth/ # OAuth logins + logout
β βββ users/ # Profiles, roles, bans
β βββ reports/ # Submit, list, moderate
β βββ flags/ # Submit, list, resolve
β βββ admin/ # Settings, analytics
β βββ moderation/ # Moderation log
βββ db/
β βββ connect.php # DB connection + session
β βββ auth_helper.php # Auth + role checks
β βββ api_response.php # Unified API response helper
βββ public/
β βββ index.html # Map + reporting form
β βββ login.html # OAuth login
β βββ user_profile.html
β βββ moderation_dashboard.html
βββ assets/
β βββ css/style.css # Shared styles
β βββ js/ # JS modules
β βββ api.js
β βββ map.js
β βββ report_submission.js
β βββ user_profile.js
β βββ moderation.js
β βββ auth.js
βββ README.md
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255),
oauth_provider VARCHAR(50),
oauth_id VARCHAR(255),
role ENUM('user','moderator','admin') DEFAULT 'user',
status ENUM('active','banned') DEFAULT 'active',
privacy ENUM('public','logged-in','private') DEFAULT 'public',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE reports (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
category_id INT,
severity_id INT,
incident_type_id INT,
description TEXT,
latitude DOUBLE,
longitude DOUBLE,
photo_url TEXT,
status ENUM('pending','approved','rejected') DEFAULT 'pending',
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
resolved_at TIMESTAMP NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE comments (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
report_id INT,
content TEXT,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE flags (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
report_id INT NULL,
comment_id INT NULL,
reason TEXT,
status ENUM('pending','dismissed','removed') DEFAULT 'pending',
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE moderation_log (
id INT AUTO_INCREMENT PRIMARY KEY,
moderator_id INT,
action_type VARCHAR(50),
target_id INT,
details TEXT,
notes TEXT,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE settings (
id INT PRIMARY KEY,
require_approval TINYINT(1) DEFAULT 1
);
CREATE TABLE categories (id INT PRIMARY KEY, name VARCHAR(50));
CREATE TABLE severity_levels (id INT PRIMARY KEY, name VARCHAR(50));
CREATE TABLE incident_types (id INT PRIMARY KEY, name VARCHAR(50));git clone https://github.com/ChadOhman/opensafetymap.git
cd accident-reports- Import schema above into MySQL
- Add reference data for categories, severity, incident types
- Create initial admin user manually
Edit db/connect.php:
$dsn = "mysql:host=localhost;dbname=accidents;charset=utf8mb4";
$user = "your_mysql_user";
$pass = "your_mysql_pass";- Update report submission API to upload photos to your S3 bucket
- Store
photo_urlin DB
Use PHPβs built-in server:
php -S localhost:8000 -t publicOpen http://localhost:8000.
When npm-based tooling is blocked by the network proxy, you can still run basic checks to avoid regressions:
python tools/html_checks.py index.html
python tools/a11y_checks.py index.htmlThese scripts verify document structure, labeling, and key accessibility affordances without downloading external packages.
- User β Submit reports, comment, flag content
- Moderator β Moderate reports & flags, add notes
- Admin β All moderator actions + manage settings, roles, bans
- Moderator performance analytics (leaderboard)
- Email notifications for approvals/rejections
- Map heatmap visualization of reports
- Export moderation logs to CSV/Excel
- Backend: PHP/MySQL
- Frontend: JS/Leaflet
- Maintainer: Your Name
MIT License.
Free to use, modify, and distribute.
