# Features — crimemapping_website

Evidence date: 2026-10-18. Source: `git@bitbucket.org:overdrive/crimemapping_website.git`, `master` at `8c3c372`.

Status words used here: **Current** (present on `master`), **Stub** (route or file exists and does not implement the named behaviour), **Unknown**. Nothing in the repository is labelled planned, in development, or deprecated by a document. Git has no feature branch. `index.old.html` and commented blocks are kept files, not a deprecation record.

## Current — public site

| Feature | What it does | Where | Provenance |
|---|---|---|---|
| Marketing page | Explains crime mapping, advantages, and a four-step "how it works" | `index.html`, `assets/` | **Implemented** |
| App and admin links | "Go to App" and "Login" open the app host and `admin.crimemapping.co.za/public/` | `index.html` | **Implemented** |
| Contact address | Shows `info@crimemapping.co.za` | `index.html` | **Implemented** |
| HTTPS redirect | HTTP requests redirect to HTTPS on the same host | `.htaccess` | **Implemented** |
| Matomo | Page view to `analytics.medmin.co.za`, site id 4 | `index.html` | **Implemented** |
| Notify-me redirect | `email.php` redirects to `/#Contact?Success=True` | `email.php` | **Implemented**. The form that would post to it is commented out in `index.html` |
| Previous landing page | Older HTML kept beside the current page | `index.old.html` | **Implemented** file. It is not linked from `index.html` |
| Saved coming-soon page | WordPress/Themify export titled "Coming soon", canonical `https://crimemapping.co.za/coming-soon/` | `org/index.html` | **Implemented** static file. It is not a second application |

## Current — admin access

| Feature | What it does | Where | Provenance |
|---|---|---|---|
| Email login | Session after `password_verify` | `Welcome::index`, `common/login` | **Implemented** |
| Logout | Destroys the session | `Welcome::logout` | **Implemented** |
| Forbidden page | View used when menu access is denied | `Welcome::forbiddenPage`, `common/forbidden` | **Implemented** |
| Self-registration | Form posts to `Welcome::registration`, stores role id 1 | `common/register`, `Welcome.php` | **Implemented**. Validation uses `users.username`, which the migration does not create |
| Empty dashboard | `home` renders a layout whose content section is empty | `Home.php`, `common/home.php` | **Implemented** |
| Menu sidebar | Categories, menus, and submenus from `user_access` for the session role | `sidebar.php`, `menu_helper.php` | **Implemented** |
| Menu management | Create category, menu, and submenu. Can write a new controller and view file | `Developers/MenuManagement.php` | **Implemented** |
| CRUD generator | Page that prints generated controller, model, and view source | `Developers/CRUDGenerator.php`, `crudGenerator.php` | **Implemented** as a generator UI |
| Profile | Signed-in user edits profile. Cellphone and password checks run in the page script | `Users::displayProfile`, `settings/profile.php` | **Implemented** |
| User list | Create, update, hard delete. Role id 4 sees only their organisation | `Users.php`, `UserModel.php` | **Implemented** |
| Roles and menu grants | Create, update, delete roles. Toggle category, menu, and submenu access | `UserRoles.php`, `UserRoleModel.php` | **Implemented** |

Seeded menu entries are Dashboard, Users, Menu Management, and CRUD Generator (`Seeds/UserMenu.php`). Crime, map, organisation, location, and incident screens are not in that seed.

## Current — crime operations

| Feature | What it does | Where | Provenance |
|---|---|---|---|
| Crime log | List, create, update, delete crimes. Map picker for coordinates. Incident badges. Linked same-day child crimes | `CrimeLog.php`, `common/crimeLogList.php` | **Implemented** |
| Verified flag | Checkbox sets `verified`, `verified_user_id`, and `verified_date`. An already verified row stays verified on update | `CrimeLogModel` | **Implemented** |
| Organisation scope | Role id 4 lists only that organisation's crimes, users, and map points | `CrimeLogModel`, `UserModel`, `CrimeMapModel` | **Implemented**. Role name **Unknown** |
| Delete button rule | Role id 1 always sees Delete. Other roles see it only while the crime is not verified | `crimeLogList.php` script | **Implemented** in the page. The delete action does not repeat the check |
| Crime map | Date range, incident-type checkboxes, attempted and all toggles, marker clusterer | `CrimeMap.php`, `common/crimeMap.php`, `public` crime map script | **Implemented** |
| GIS map page | Full-height map, eight sidebar placeholders, hidden session email | `CrimeMapGis.php`, `common/crimeMapGis.php` | **Implemented** |
| Crime log export | Same list plus a note column and DataTables copy/csv/excel/pdf/print | `CrimeLogExport.php`, `crimeLogListExport.php` | **Implemented** |
| Incident types | Name, note, active. Delete sets `active` 0. Map reads `attempted`, `name_group`, and `color` | `IncidentType.php`, `IncidentTypeModel.php` | **Implemented**. Those three columns are not in the migration and are not written by create |
| Organisations | Name, contact, address, town, note, active, parent/child orgs, many suburbs | `Organisations.php` | **Implemented** |
| Locations | Municipal boundary attributes (`sp`, `mp`, `mn`, `dc`, shape length and area), list capped at 10,000 | `Locations.php`, `LocationsModel.php` | **Implemented**. No migration for `locations` |
| Activity log | Create, update, hard delete screens | `ActivityLog.php` | **Implemented** screens. No caller from login or crime capture was found |
| Crime photos in admin | `getCrimeImages` returns `image_log` paths for a crime and its children | `CrimeLog::getCrimeImages` | **Implemented** read. The admin create action that was read does not upload a file |

## Current — HTTP API

Resource routes are declared in `Routes.php`. CI4 resource routes expose index, show, create, update, and delete unless the method is absent. Custom methods are reachable while auto-routing is on.

| Feature | What it does | Where | Provenance |
|---|---|---|---|
| Cellphone login | JSON cellphone and password. Returns user id and JWT. Saves JWT on `users.token` | `Apiusers::login` | **Implemented** |
| Logout | Clears `users.token` for the bearer token | `Apiusers::logout` | **Implemented** |
| Current user | Returns the user row for the bearer token | `Apiusers::getUser` | **Implemented** |
| User CRUD | Index, show, create, update, delete behind JWT | `Apiusers.php` | **Implemented**. Login itself does not require a prior token |
| Create crime | JWT, form fields, incident JSON, optional images | `Apicrimes::create` | **Implemented**. Update and delete on this controller are commented out |
| Read crime | Show returns the crime plus incidents and images. Index calls `getUsers` on the crime model | `Apicrimes::index`, `show` | **Implemented**. The index method name does not match "list crimes" |
| Incident catalogue API | JWT list and show. Create, update, delete, and a `login` method exist on the same controller | `ApiIncidents.php` | **Implemented** |
| Organisation API | `index` returns every organisation with no JWT check. Show and writes check JWT | `ApiOrganisations.php` | **Implemented** |
| GIS authenticate | Vendor name plus `api_key` checked as a user password | `Apigis::authenticate` | **Implemented** |
| Active towns | Counts organisations grouped by `town` | `Apigis::getActiveTowns` | **Implemented** |
| User location | Email to organisation town and suburbs | `Apigis::getUserLocation` | **Implemented** |
| Town crime log | Paged point features, 100 per page | `Apigis::getTownCrimeLog` | **Implemented** |

## Current — mobile build

The React source is not in this repository. It is `git@bitbucket.org:overdrive/crimemapping.git` (`5927ab4`), documented in `silicon_ark/crimemapping/`. The production bundle is here.

| Feature | What it does | Where | Provenance |
|---|---|---|---|
| PWA shell | Manifest name "Crime Mapping Mobile Application", standalone display | `app.crimemapping.co.za/manifest.json` | **Implemented** |
| Routes | `/`, `/login`, `/signup`, `/forgot-password`, `/report-crime`, `/unauth`, `*` | `static/js/main.f6fb2877.js` | **Implemented** in the bundle |
| Report copy | Strings include "Report a Crime", "Create Crime Event", "Log Crime Event", "Upload Photo", "Remove Image", "New Crime" | same file | **Implemented** strings |
| Password hint | "Password length should be more than 7" and "Passwords do not match" | same file | **Implemented** strings |
| API base | `REACT_APP_PUBLIC_API_URL` in `.env` is `https://admin.crimemapping.co.za/public`. The same host is hardcoded in the bundle | `.env`, bundle | **Implemented** |
| Service worker | `index.html` registers `serviceWorker.js`. The file in the tree is `service-worker.js` | `index.html` | **Implemented** mismatch |

## Stub

| Feature | Evidence | Provenance |
|---|---|---|
| API forgot password | `Apiusers::ForgotPassword` and `ApiIncidents::ForgotPassword` have empty bodies | **Implemented** as empty methods |
| API change password | `Apiusers::ChangePassword` and `ApiIncidents::ChangePassword` have empty bodies | **Implemented** as empty methods |
| Admin home widgets | `common/home.php` content section is empty | **Implemented** |
| AWS, mPDF, PhpSpreadsheet | Required in `composer.json`. No call under `app/` | **Unknown** runtime use |

## Not in this repository

- React source for the mobile client. That source is the separate repository `overdrive/crimemapping`. No native project was found there either.
- A migration or seed for role id 4, incident `attempted` / `name_group` / `color`, `locations`, or `org_location_map`.
- `sonarqube_scan.bat` (named in the OVER-78 scan inventory, absent from `git ls-files` at `8c3c372`).
- A written roadmap. The admin README links to Jira project `SI` and wiki space `AO` on `siliconsoftware.atlassian.net`. Those sites were not opened.
