# PRD — public-procon8.co.za

Evidence date: 2026-10-06. Source: `git@bitbucket.org:overdrive/public-procon8.co.za.git` at `f3f3b6d` (2020-08-27, `master`).

There is no prior PRD in the repository or in Silicon Ark. Requirements below were recovered from the PHP, HTML, and committed configuration. Provenance is marked on each item.

## Product purpose

PROCON8 CONSULTING SA publishes a public brochure site. The home meta description says the company provides project-control and contract-management expertise to the engineering, construction, and manufacturing industries. Provenance: **Documented** (`index.php`).

The site also describes a related reporting product, Quantivia (Deliverable Based Reporting), and links to `http://www.quantivia.com`. Quantivia's application is not in this repository. Provenance: **Documented** (`dbr.php`, navigation in `content.php`).

`SLA.php` titles the page PROCON8 MANAGEMENT LIMITED and embeds an external dashboard. That name conflicts with PROCON8 CONSULTING SA used everywhere else. Provenance: **Documented** on `SLA.php`. Requires confirmation which name is current.

## Users and roles

| Role | What they do | Evidence | Provenance |
|---|---|---|---|
| Public visitor | Reads the brochure, moves between panels, opens reference-project links, may submit the contact form | `index.php`, section fragments | Implemented |
| Enquiry recipient | Receives the contact email at `support@procon8.com` | `thanks.php` | Implemented as a mailbox address. No staff login exists in this repo |
| Dashboard viewer | Opens `SLA.php` directly and sees an iframe | `SLA.php` | Implemented page. Not linked from the menu. Who is meant to open it is Unknown |

There are no accounts, sessions, passwords, or permission checks in application PHP.

## Information the site manages

The site does not store enquiries in a database. A successful contact post is sent as email. The GoDaddy form scripts can write posted fields to files under a `data/` directory, but the contact form does not call them.

Marketing content is hardcoded in PHP fragments and in `content.php`.

## Main workflows

### W1 — Read the brochure

Trigger: visitor opens `index.php`.

Behaviour:

1. `index.php` turns on gzip output buffering and limits PHP error reporting to `E_ERROR` with `display_errors` off.
2. It builds a `kwassie` object and prints the shell: logo `images/logo.png`, a Twitter icon linking to `http://www.twitter.com/`, and a LinkedIn Follow Company widget for company id `2761141`.
3. The top menu is built from `content.php` `navigator_bar_options`. Labels, in order: a home icon, About us, Contact us, Overview, Project Control, Contract Management, Specialist Services, Quantivia™, Reference Projects.
4. Menu anchors use `href="#"` and a `data-id` equal to the target filename with the dot removed (`aboutus.php` becomes `aboutusphp`). `js/nav.js` hides every `.pbox` and shows the one whose id matches.
5. A fade slideshow cycles `images/slide1.jpg` through `images/slide12.jpg` at 4000 ms (`js/galleryscript.js`, size 977×314).
6. The first panel (home) is server-rendered inside `index.php`, not loaded from a fragment. Later panels `include` the matching PHP file and start hidden.
7. The footer repeats the same destinations. The first footer label is the bare PHP token `Home` in `create_footnav`, not the home image. See edge cases.

Provenance: **Implemented**.

Home copy states the company supplies project control and contract management, names sectors (civil, building, infrastructure, defence, rail, aviation), and states a commitment to value for money, open reporting, and mentoring. "Read more" links reload `index.php?show=...`. Provenance: **Documented** on the home panel.

### W2 — Open a panel from a URL

Trigger: `index.php?show=<id>`, for example `index.php?show=project_controlphp`.

If `$_GET['show']` is non-empty, `index.php` prints a ready-handler that hides visible `.pbox` elements and shows `.pbox[id=<value>]`. The value is concatenated into the script with no encoding.

`nav.js` also runs on ready and clicks the first menu link, which shows the home panel. Both handlers run. Which panel the visitor actually sees depends on script order and is **Unknown / Requires Confirmation** in a browser. The home "Read more" links rely on this query parameter.

Provenance: **Implemented**.

### W3 — Send a contact enquiry

Trigger: visitor opens the Contact us panel and submits `contact_us_form`.

The form posts to `thanks.php` with fields `user_name`, `email`, `phone`, `country`, `comments`, the submit control `contact_us_submit`, and a reCAPTCHA widget.

Client checks in `contact_us_validate` (`contactus.php`):

- Name, email, phone, and country must be non-empty.
- Email must match `/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i` (top-level domain limited to 2–4 letters).
- The function then reads `frm.subject`. The form has no control named `subject`. The comments box is named `comments`. There is no `return true`.

Server behaviour (`thanks.php`), in order:

1. POST `g-recaptcha-response` to `https://www.google.com/recaptcha/api/siteverify` with the secret hardcoded in the file.
2. If `success` is false, stop with the text "The reCAPTCHA wasn't entered correctly. Go back and try it again."
3. If `success` is true and `contact_us_submit` is present, trim, strip slashes, and `htmlspecialchars` each field. The mail subject is the fixed string "Contact us". The visitor's comments are placed in the HTML body under the label "Subject".
4. Send with PHPMailer 5.2.27 over SMTP to Amazon SES (`email-smtp.eu-west-1.amazonaws.com`, port 587, TLS, SMTP auth).
5. Envelope From and To are both `support@procon8.com`. The visitor's name is the From display name. Reply-To is `visitor name <visitor email>`. A custom header `X-SES-CONFIGURATION-SET` is set to `ConfigSet_www_procon8_co_za`.
6. On send success, print "Your message was successfully sent! Thank you for contacting us, we will reply to your inquiry as soon as possible!" and a link to `index.php`.
7. On send failure, print "Email not sent." plus PHPMailer's `ErrorInfo`, and the same home link.

`thanks.php` is a bare response. It does not include the site header, menu, or footer.

The server does not repeat the client required-field checks. A direct POST with the submit control and a passing captcha can send empty fields.

A commented assignment in `thanks.php` shows an alternate recipient `heinrich@overdrive.co.za`. It is not used. Provenance: **Implemented** as a comment only.

Provenance of the live path: **Implemented**.

### W4 — Read a service page

Trigger: visitor selects a service panel or a sidebar link.

Sidebar lists live in `content.php` and are printed by About, Overview, and Specialist Services:

- Project Control links all go to `index.php?show=project_controlphp` (Planning & Scheduling, Cost Control, Claims Analysis, Material Control, Change Control, Risk Management, Reporting).
- Contract Management links all go to `index.php?show=contract_managementphp` (Contract Changes, Claims & Disputes, Progress Payments, Insurance & Bonding, Correspondence Control).
- Specialist Services links go to anchors: Tender Preparation (`specialist_servicesphp#tender`), Deliverable Based Reporting (`dbrphp#engg`), S-Curve Reporting, Management Schedules, Six Sigma, Coaching / Mentoring / Training.

The Deliverable Based Reporting sidebar item targets anchor `engg` on `dbr.php`, whose heading is "Project Management with the Lights On", not a heading named Deliverable Based Reporting. The page title heading is "Deliverable Based Reporting (Quantivia™)".

`specialist_services.php` also has a section "Critical Issues Reporting" (`#critical`) that is not in the sidebar list. "Engineering Earned Value" (`#engg` on the specialist page) is not in the sidebar. The sidebar's Deliverable Based Reporting item uses the same anchor id `engg` on a different page.

Provenance: **Implemented**.

Service pages are marketing descriptions. They do not collect data or run project-control calculations. The project-control and contract-management pages list disciplines the company says it provides (cost, schedule, change, risk, claims, payments, insurance, correspondence, subcontract administration). Provenance: **Documented** in those PHP files.

### W5 — Open Quantivia material

Trigger: visitor selects Quantivia™ in the menu, or the Deliverable Based Reporting sidebar link.

`dbr.php` explains why the company says projects fail at scheduling, shows `images/MWTLO.png` and a second slideshow (`images/dbr_1.png` through `dbr_10.png`, 977×549), lists questions Quantivia helps with, and lists product benefits (remote login, dashboard, calendar, access restrictions, change control, risk register, and others). Two "View Online" links point at `http://www.quantivia.com`.

Those benefits describe Quantivia. They are not features implemented in this repository.

Provenance: **Documented** on `dbr.php`.

### W6 — Reference projects

Trigger: visitor opens the Reference Projects panel.

The page says the list is projects employees have been directly involved with. Each row is four parallel lists: project name, city, country, and an external URL (opened in a new tab). Groups:

| Group | Entries present in the HTML |
|---|---|
| RAIL - PROJECTS | Gautrain Rapid Rail Link; Hong Kong West Rail; Cross Country Route Modernization; Cross Rail; Jubilee Line Extension Project; Portland Light Rail Project; King Abdulah Financial District; Expresso Tiradentes; GoldLinkQ |
| AVIATION - PROJECTS | New Doha International Airport; New Hong Kong International Airport |
| MANUFACTURING - PROJECTS | SBB Twindexx FV Dosto (Zurich, Switzerland) |
| BUILDING | Galleria Shopping Centre; Sanlam Head Office (both Cape Town, South Africa). Two further list items are `&nbsp;` |
| RAIL - BIDS | GoldLinkQ (Gold Coast); Sydney Metro; Hiawatha Light Rail Proposal (Minneapolis); National Signalling Programme (Birmingham, UK); National Signalling Programme (Johannesburg, South Africa) |
| INFRASTRUCTURE - BIDS | Lusail Project; New Doha Port (both Doha, Qatar) |

Names are copied as spelled in `reference_projects.php`. Provenance: **Documented**.

### W7 — SLA dashboard page

Trigger: visitor requests `SLA.php` by URL. Nothing in the menu or footer points here.

The page is a full HTML document, not a fragment. It sets the same error reporting as `index.php`, loads the same CSS and scripts, then shows an iframe:

`http://www.procon8.co.za:9090/publishPage.do?method=getPublishedDashboard&pagekey=0656059555200eb796972296434984a9`

Width 988 px, height 730 px. The menu markup is not printed. `$brush` is constructed and not used in the body.

Provenance: **Implemented**. The system behind port 9090 is **Unknown / Requires Confirmation**.

## Functional requirements

### FR-001 — Public brochure shell

The system serves one HTML page (`index.php`) that contains every menu section, shows the home section first, and reveals other sections without a full navigation reload when a menu item is clicked.

Actor: public visitor. Trigger: GET `index.php` or a menu click.

Inputs: none required.

Output: XHTML 1.0 Transitional page, title "PROCON8 CONSULTING SA", stylesheet `css/style.css` and `fonts.css`.

Evidence: `index.php`, `js/nav.js`, `content.php`. Implementation: Implemented. Confidence: High.

### FR-002 — Section catalogue

The menu and the included fragments are exactly the nine entries in `navigator_bar_options`. Adding a page requires an entry there and a matching file; there is no database of pages.

Evidence: `content.php`. Implementation: Implemented. Confidence: High.

### FR-003 — Home summary and commitment

The home panel shows four teasers (Project Control, Contract Management, Specialist Services, Reference Projects), an about-company paragraph, and a commitment quotation. Each teaser links to `index.php?show=<panel>`.

Evidence: `index.php` home markup. Implementation: Implemented. Confidence: High.

### FR-004 — Contact form fields and client checks

The contact panel shows name, email, phone, country, comments, a reCAPTCHA widget, and a submit control. Client script blocks submit when name, email, phone, or country is empty, or when email fails the pattern above.

Evidence: `contactus.php`. Implementation: Implemented. Confidence: High for the checks that name real fields. The `frm.subject` check does not name a real field. See edge cases.

### FR-005 — Captcha gate before mail

`thanks.php` verifies `g-recaptcha-response` with Google `siteverify` before sending mail. A failed verify does not send mail.

Evidence: `thanks.php`, `recaptcha_get_html` in `recaptchalib.php` (widget markup). Implementation: Implemented. Confidence: High for the code path. Whether the committed `php.ini` (`allow_url_fopen = off`) is loaded on the host, which would stop `file_get_contents` from calling Google, is Unknown.

### FR-006 — Enquiry email

After captcha success, the system emails `support@procon8.com` an HTML table of name, email, phone, country, and comments, using SES SMTP as configured in `thanks.php`.

Evidence: `thanks.php`. Implementation: Implemented. Confidence: High for the code. Whether SES still accepts the committed credentials is Unknown. Credential values are not recorded in this PRD.

### FR-007 — Enquiry acknowledgement

The visitor sees a success sentence and a home link when PHPMailer reports success, or "Email not sent." plus the library error when it does not.

Evidence: `thanks.php`. Implementation: Implemented. Confidence: High.

### FR-008 — Service catalogues

Project Control and Contract Management pages list the disciplines in their heading columns. Specialist Services pages describe Tender Preparation, Engineering Earned Value, S-Curve Reporting, Critical Issues Reporting, Management Schedules, Six Sigma, and Coaching / Mentoring / Training.

Evidence: `project_control.php`, `contract_management.php`, `specialist_services.php`. Implementation: Implemented as static copy. Confidence: High.

### FR-009 — Quantivia referral

The Quantivia panel describes the product and links to `http://www.quantivia.com`. It does not log the visitor into Quantivia.

Evidence: `dbr.php`. Implementation: Implemented. Confidence: High.

### FR-010 — Reference project list

The reference panel lists the groups and rows in W6, with external links in a new tab.

Evidence: `reference_projects.php`. Implementation: Implemented. Confidence: High.

### FR-011 — Company positioning copy

About and Overview state that employees have a minimum of 15 years' relevant experience, that work follows DMAIC (Define, Measure, Analyse, Implement, Control), that a project baseline is typically set within three months of Notice to Proceed, and that a common work breakdown structure ties scope, cost, schedule, change, and risk.

These statements are page copy. The site does not check years of experience, run DMAIC, or store a baseline.

Evidence: `aboutus.php`, `Overview.php`. Provenance: **Documented**. Confidence: High that the copy exists. Confidence that it is an enforced business rule: not applicable; it is not implemented as software behaviour.

## Integrations

| System | Purpose | Trigger | Direction | Auth | Failure |
|---|---|---|---|---|---|
| Google reCAPTCHA | Widget and `siteverify` | Contact panel render; form POST | Browser loads `api.js`. Server POSTs the token | Site key in `contactus.php`. Secret in `thanks.php`. Values not copied here | Failed verify: die with the retry sentence. Network failure is not handled; `file_get_contents` failure would make `json_decode` fail. No user-facing branch for that |
| Amazon SES SMTP | Deliver the enquiry | Captcha success and submit control present | Outbound SMTP, eu-west-1, port 587, TLS | SMTP username and password hardcoded in `thanks.php` | PHPMailer error string shown to the visitor |
| LinkedIn | Follow Company button | Every `index.php` view | Browser loads `platform.linkedin.com` | Company id `2761141` in the page | Not handled. Widget fails independently of the page |
| Quantivia website | "View Online" and the product name link | Visitor click | Browser navigates to `http://www.quantivia.com` | None in this repo | Not handled |
| Dashboard on port 9090 | iframe on `SLA.php` | Direct request for `SLA.php` | Browser loads `http://www.procon8.co.za:9090/publishPage.do` | A `pagekey` query value is in the iframe URL | Not handled. Broken iframe would show an empty frame |
| Reference-project sites | Outbound links | Visitor click | Browser, new tab | None | Not handled |

PHP `mail()` is present only in `thanks.php.bak` and in comments inside `thanks.php`. The live file uses PHPMailer.

## Constraints

- Single commit on `master`. No branches, no CI config, no README, no `composer.json`.
- Pages are XHTML-era markup with table layout and presentational class names (`myrreg_*`).
- `index.php` suppresses display of errors. Included fragments do not set that themselves; they inherit it when included from `index.php`. `thanks.php` does not.
- `.htaccess` at the web root is empty. `missing.html` is a GoDaddy 404 template that loads CSS from `secureserver.net`. Whether the host uses it is Unknown.
- Committed `php.ini` sets `allow_url_fopen = off`, `register_globals = off`, `expose_php = Off`, and `extension=imap.so`. `thanks.php` depends on `allow_url_fopen` for `siteverify`. If this ini file is the one PHP loads, captcha verification cannot run. Whether the host loads it is Unknown.

## Edge cases

| Case | Behaviour in code | Provenance |
|---|---|---|
| `?show=` value interpolated into JavaScript | No `json_encode` or escaping | Implemented. Treat as a defect. Production exposure Unknown |
| Client validator reads `frm.subject` | No such field. Reaching that line throws. The function never returns true. Whether the browser still submits is Unknown / Requires Confirmation | Implemented mismatch |
| Comments left empty | Not validated, client or server | Implemented |
| Captcha passes but `contact_us_submit` is absent | No mail and no message; response body is empty | Implemented |
| Captcha HTTP call fails | No dedicated error message | Implemented gap |
| `php.ini` `allow_url_fopen = off` applied | `siteverify` via `file_get_contents` cannot run | Conflict between `php.ini` and `thanks.php`. Requires confirmation |
| Footer constant `Home` | Unquoted constant. PHP 7 and earlier: notice, text "Home", hidden on `index.php` because only `E_ERROR` is reported. PHP 8+: Error for an undefined constant, which can break the footer | Implemented. Host PHP version Unknown |
| `$return` used before assignment in `contactus.php` | Notice on first concatenation, then the form HTML. Hidden when included from `index.php` | Implemented |
| Direct request for a fragment such as `aboutus.php` | Fragment has no HTML shell. It still includes `common_functions.php` and prints the section only | Implemented |
| `read_page_content` | Would HTTP-GET `http://127.0.0.1/<file>` and cut on `<!--Content-->` markers. The call in `index.php` is commented out. No fragment contains those markers | Implemented method, unused |
| `create_list` | Builds a list from an array. No caller in the application pages | Implemented method, unused |
| `gdform.php`, `webformmailer.php`, `cgi/gdform.cgi` | Write request fields to a data file and redirect. Contact form does not post to them. `webformmailer.php` exits immediately unless `$_SERVER['SPI']` is set | Present, not on the contact path |
| Backup files | `index.php.bak` is byte-identical to `index.php`. `thanks.php.bak` uses PHP `mail()` and `recaptcha_check_answer` (old challenge fields). `contactus.php.bak` and `recaptchalib.php.bak` differ from the live files. None are linked | Implemented as files only |
| `disclaimer.html` | Static email disclaimer (confidentiality, viruses, contracts, negligent misstatement, employer liability). Not linked from the menu. Not added to the contact email body | Documented text, not wired |
| Duplicate ids | Slideshow markup uses ids `banner` and `bannerOne` on both `index.php` and `dbr.php`. Both can be in the DOM together | Implemented |
| Building reference rows | Two `&nbsp;` list items | Documented in the HTML |

## Non-goals visible from the code

The repository does not implement project scheduling, cost control, contract administration, user login, or Quantivia itself. Those appear only as marketing copy or as an outbound link.
