# Technical documentation — supplier.sarugby.online

Evidence date: 2026-10-01. Source: `master` at `6f0e6c2` (2021-04-29).

This document describes the tree that was inspected. It does not prescribe a target architecture.

## Stack

| Item | Evidence |
|---|---|
| Language | PHP. Front controller sets `error_reporting` to `E_ALL\|E_STRICT` and `display_errors` to 1. |
| Framework | Zend Framework 1, vendored in `library/Zend`. Application class is `Zend_Application`. Controllers extend `Zend_Controller_Action`. Models extend `Zend_Db_Table`. |
| UI | Limitless theme (Bootstrap) under `public/assets` and `public/global_assets`. Views are `.phtml`. jQuery posts JSON-style form payloads. |
| Database | `pdo_mysql`. Host `db.footprintapp.net`. Database name `teamwizz_SARU`. Username is in `application.ini`. Password is in the same file and is **not repeated here**. |
| Web server | Apache `mod_rewrite`. CodeDeploy install hook installs `httpd` with `yum` and the start hook runs `service httpd start`. |
| PDF | Dompdf under `public/dompdf`, required from `IndexController` as `dompdf/dompdf_config.inc.php`. |
| Deploy | Bitbucket Pipelines image `python:3.5.1`, boto3 `1.3.0`, AWS CodeDeploy. |
| Time | `date_default_timezone_set('Africa/Johannesburg')` in `public/index.php`. |
| Locale | `resources.locale.default = af_ZA`. |
| Session | `resources.session.cookie_lifetime = 864000`. |

PHP version on the server is **Unknown**. The code calls `mcrypt_create_iv`, which is not in PHP 7.2 or later unless the `mcrypt` extension is installed separately.

## Request path

1. Root `.htaccess` rewrites any URI that does not start with `/public/` to `public/{uri}`. The HTTPS-to-www rules in that file are commented out.
2. `public/.htaccess` serves real files and directories, and rewrites everything else to `index.php`.
3. `public/index.php` defines `APPLICATION_PATH`, `APPLICATION_ENV` (environment variable or `production`), and `UPDATE_PATH` (`public/archives`). It bootstraps `application/configs/application.ini`.
4. `resources.frontController.moduleDirectory` is `application/modules`. The only module is `default`.
5. `PG_Controller_Plugin_LayoutPicker` sets the layout name to the module name, so the layout file is `default.phtml` when the module is `default`. `application.ini` also sets `resources.layout.layout = default`. Keys `admin`, `union`, `referee`, `gir`, and `accreditations` name other layouts. Those layout files are not in `application/layouts/` (only `default.phtml` and `layout.phtml` are). Provenance: **Implemented** configuration for modules this repository does not contain.

`Default_Bootstrap` in `application/modules/default/controllers/Bootstrap.php` redirects to HTTPS when the port is not 443. ZF1 loads `{Module}_Bootstrap` from the module when the module bootstraps. Confidence that this runs on a normal request: medium. It was not executed here.

## Application structure

```text
supplier.sarugby.online/
  public/index.php          front controller
  public/.htaccess          rewrite to index.php
  .htaccess                 rewrite into public/
  application/
    Bootstrap.php           empty _initDbAdapter
    configs/application.ini
    layouts/default.phtml   authenticated shell
    models/                 Zend_Db_Table classes
    modules/default/
      controllers/          one module
      views/scripts/        login, register, index, permits, company, users, error
  library/Zend/             framework
  library/PG/               local plugins
  scripts/                  CodeDeploy hooks
  appspec.yml
  bitbucket-pipelines.yml
  codedeploy_deploy.py
```

Controllers and the actions that do work:

| Controller | Actions with behaviour |
|---|---|
| `LoginController` | `index`, `forgot`, `reset`, `logout`, `companypic`, `uploadremotecompany` |
| `RegisterController` | `index`, `newcompany`, `validateemail` |
| `IndexController` | `index` (dashboard), `getpermitpdf`, `newcompany` (unused by views), `test` |
| `PermitsController` | `create`, `addpermit`, `profile`, `savepermit`, `duplicatepermit`, `cancel`, `questions` |
| `CompanyController` | `profile`, `upload` |
| `UsersController` | `index`, `add`, `profile` |
| `ErrorController` | `error` |

`OrganisationController`, `OrgController`, and `AuthonController` do not implement a finished workflow. `EventsController.php` declares `Companies_EventsController`, which does not match the file name Zend would instantiate for controller `events`.

## Data

There is no SQL schema, migration, or seed in the repository. Table and view names below are the `$_name` values and the SQL fragments in the models that the controllers actually use.

| Name | Used as | Touched by |
|---|---|---|
| `CompaniesLogin` | Auth identity view or table. Columns read: `Email`, `password`, and the whole row stored as the identity (`MID`, `CID`, `FirstName`, `TradingName` are used later). | Login |
| `Companies_Profile` | Company row. Key `CID` in updates. | Registration, company profile |
| `Company_Members` | People. Key `MID`. | Registration, staff, password reset, occupant match on `IDNumber` |
| `Company_Events` | Link of company to event. Inserts `CID`, `EID`, `Status`. | Registration. Dashboard counts via `getEventCount`. |
| `Accreditation_List` | Event. Key used as `AC`. `PreFix` builds the permit number. | Dashboard, `getRef` |
| `Accreditations_view` | Event row including load-in and load-out active dates. | Permit create |
| `Accreditation_Permits` | Permit row. Key `PID`. | Create, update, cancel, duplicate |
| `Permits_view` | Permit plus joined display fields (`PermitType`, `Email`, `FirstName`, `TimeIn`, `TimeOut`, `Status`, `LoadType`, `RequestDate`, `Accupants`, active dates). | Dashboard, profile, notification email |
| `Permit_Status` | `getStatus()` reads it. No controller in this module calls `getStatus`. | Model only |
| `Accreditation_Load_Services_Types` | Service type dropdown. | Permit form |
| `Accreditation_Loading_Zones` | Zones for the event (`AID`). | Dashboard |
| `EventGates` | Model `AccreditationGates`. Instantiated in permit actions and not passed to the view in the portion read. | Constructed, unused in those actions |
| `vehicletypes` | Vehicle type dropdown. | Permit form |
| `BBEE_Level` | B-BBEE level list. | Registration and company profile |
| `Accre_Company_Types` | Company type list. | Registration and company profile |
| `countries` | Country list. | Registration and company profile |

Models that exist and are not constructed by any controller are listed in `features.md` (F-031). They point at rugby, food, news, and union tables in the same database. Provenance: **Inferred** leftover from a shared SARU codebase. `track.mypass.zone` in Silicon Ark uses the same database name.

Column lists for inserts are whatever the form posts plus the fields the controller sets. A full column dictionary is **Unknown** without the database.

### Permit status values shown in the UI

| Stored value | Label in `index/index.phtml` | Written by this repository |
|---|---|---|
| less than 4 | Pending | 2 on create and on duplicate |
| 4 | Approved | No |
| 5 | Cancelled | Yes, `cancelAction` |
| 6 | Declined | No |
| 7 | Checked-In | No |
| 8 | Delayed | No |
| 9 | Checked-Out | No |

`Permit_Status` may hold the official names. This repository does not render that table on the dashboard; the labels above are hard-coded.

## Authentication

- Adapter: `Zend_Auth_Adapter_DbTable`.
- Storage: default ZF1 auth storage (session).
- Password comparison: the controller MD5s the password first, and the adapter does not hash again.
- Override: see business rule BR-003. Do not copy the string into new systems.
- Session event: `Zend_Session_Namespace('EVENT')->EID = 61` inside `LoginNow`, after a successful authenticate.
- After login, `Model_CompanyProfile::updateData` is asked to set `LastLogin`. The method writes `WHERE CID = '{second argument}'`. The caller passes `MID ='{member id}'` as that argument, so the predicate is not a numeric `CID`. **Inferred:** the login time is not stored by this call. Which table actually has `LastLogin` is **Unknown**.

## Mail

Every `sendMail` in Login, Register, Index, and Permits builds `Zend_Mail_Transport_Smtp` to host `mail.sarugby.co.za`, port 25, auth login, username `accreditation@sarugby.co.za`. The password is hard-coded in each of those four methods and is **not repeated here**. From-address is the same mailbox, display name "Registration at sarugby.co.za". Subject is "SARugby Notification". `LoginController` sets `SMTPSecure = tls` on the mail object; the Permits copy of `sendMail` leaves that line commented out.

`PermitsController::postEmailToFp` can POST `Email`, `FullName`, and `Content` to `https://footprintapp.net/email/send`. `addpermitAction` does not call it.

## Files and storage

| Path | Contents |
|---|---|
| `public/archives/companies/{CID}_image.jpg` | Company logo. `UPDATE_PATH` is `public/archives`. |
| `public/archives/accreditation/{MID}_image.jpg` | User photo. The layout reads this for the header avatar. |
| `AC_PATH/loadin/pdf/permit_{id}.pdf` | Permit PDF written by `getpermitpdfAction`. `AC_PATH` is not defined in the repository. |

`.gitignore` ignores `public/archives/` and `public/files/pics/`.

`login/companypic` sets `Content-type: image/gif` and echoes JPEG bytes. Provenance: **Implemented**.

## Deployment

`appspec.yml` (version 0.0, os linux):

- Copies `/` to `/mnt/data/www/html/sarugby.online/supplier.sarugby.online/`.
- Owner and group `apache`.
- `BeforeInstall`: `scripts/install_dependencies` (`yum install -y httpd`), run as root, timeout 300.
- `ApplicationStop`: `scripts/remove_archives.sh` deletes CodeDeploy deployment-root directories other than the last successful install. It does not delete `public/archives`.
- `ApplicationStart`: `scripts/start_server` (`service httpd start`).

`scripts/stop_server` exists and is not referenced by `appspec.yml`.

`bitbucket-pipelines.yml` runs on branches `qa` and `prod`. Each step exports AWS and CodeDeploy names from `QA_*` or `PROD_*` repository variables, installs `zip` and `boto3==1.3.0`, zips the workspace to `/tmp/artifact.zip`, and runs `codedeploy_deploy.py`.

`codedeploy_deploy.py` uploads that zip to `s3://{S3_BUCKET}/{APPLICATION_NAME}/{timestamp}-bitbucket_builds.zip` and calls `create_deployment`. It passes `deploymentConfigName` from environment variable `DEPLOYMENT_CONFIG`. The pipeline file does not export `DEPLOYMENT_CONFIG`. Provenance: **Implemented** mismatch. A deploy can fail until that variable is set in the pipeline environment.

The pipeline does not run tests. There is no `composer.json` for the application (Dompdf has its own). Dependencies are the vendored `library/Zend` tree.

## Security notes (evidence, not a procedure)

These are defects visible in the source. Values are omitted on purpose.

1. Database username and password are committed in `application/configs/application.ini`. The same block is inherited by staging, testing, and development.
2. The mailbox password is committed in four `sendMail` methods.
3. A hardcoded password in `LoginController::indexAction` authenticates as any email that exists in `CompaniesLogin`.
4. Password reset emails the new password in clear text. Reset and registration links are `http://`.
5. Passwords are MD5.
6. SQL is built by concatenating request parameters (`fetchRow("Email ='" . $email . "'")` and similar).
7. `cancelAction`, `duplicatepermitAction`, and `UsersController::profileAction` do not check company ownership. `getpermitpdfAction` does not check that the permit id belongs to the company.
8. `uploadremotecompanyAction` accepts a company id and a base64 image without an auth check in that controller.
9. `public/index.php` displays errors. Several actions `print_r` exceptions or the word `LOGIN`.
10. `PermitsController::emailtestAction` sends mail to a hardcoded personal address. The address is omitted.

A human should rotate the database password, the mailbox password, and the login override, and should treat this Git history as containing those secrets.

## Background work, queues, monitoring

No cron, queue worker, or scheduler is in the repository. No monitoring agent is configured in the tree. SonarQube project files named in the onboarding brief are not in this commit.

## Development and testing

No README, no PHPUnit configuration, and no application tests. `test/` contains Plesk sample pages (`test/fcgi`, `test/php`, `test/ssi`).

Local run instructions are not documented in the repository. From the code, a local run needs Apache with the document root such that the root `.htaccess` can see `public/`, PHP with PDO MySQL and mcrypt, and network access to `db.footprintapp.net` using the committed configuration. That database is shared. Pointing a local checkout at it is an operational decision, not something this document recommends.

## Relationship to track.mypass.zone

Silicon Ark `track.mypass.zone/` describes a Zend Framework 1 tree with the same database name, the same permit models, and a later event id (`367` as of 2022-08-25). Differences that are visible by comparing the two documents:

| | supplier.sarugby.online (`6f0e6c2`, 2021-04-29) | track.mypass.zone (documented separately) |
|---|---|---|
| Login | Company email and password | Shared PIN on the operations screen |
| Session event | Hard-coded 61 at login; registration uses 44 | Later changed to 367 |
| Layout title | SA Rugby - Load-in and Load-out | MyPass Track |
| FAQ event name | HSBC World Rugby Sevens Series | Later changed to RWC Sevens 2022 |
| Deploy path | `/mnt/data/www/html/sarugby.online/supplier.sarugby.online/` | `/mnt/data/www/html/mypass.zone/track.mypass.zone/` |

Whether production traffic moved from this repository to that one is **Unknown / Requires Confirmation**.
