# Technical documentation — psl2020

Evidence date: 2026-10-06. Source: `git@bitbucket.org:overdrive/psl2020.git`, `master` at `007bc5a` (2020-08-04).

Secrets are named, not copied. `application.ini.bak` and `ApplicationController::sendMail` contain passwords. Those values are omitted here and should be treated as exposed in Git history.

## Stack

| Piece | Evidence | Provenance |
|---|---|---|
| PHP | `public/index.php`, controllers. `mcrypt_create_iv` requires the `mcrypt` extension (core through PHP 7.1) | **Implemented**. Exact PHP version on the server is **Unknown** |
| Zend Framework | `library/Zend/Version.php` constant `VERSION` = `1.11.10` | **Implemented** |
| MySQL via PDO | `resources.db.adapter = "pdo_mysql"` in `application.ini.bak` | **Documented** in the bak file |
| Apache | `scripts/install_dependencies` runs `yum install -y httpd`. `scripts/start_server` runs `service httpd start` | **Implemented** |
| Front end | Bootstrap, jQuery 3.1.1, jQuery Steps, jQuery Validate, Cropper, Inspinia assets under `public/assets` | **Implemented** |
| Deploy | Bitbucket Pipelines image `python:3.5.1`, `boto3==1.3.0`, AWS CodeDeploy | **Implemented** in `bitbucket-pipelines.yml` |

There is no Composer file, no `package.json`, and no PHPUnit (or other) test suite.

## Request path

1. Root `.htaccess` rewrites every URI that does not already start with `/public/` to `public/{uri}`. HTTPS and `www` redirects are commented out.
2. `public/.htaccess` serves real files and otherwise rewrites to `public/index.php` (standard Zend rewrite).
3. `public/index.php` sets the time zone to `Africa/Johannesburg`, defines `APPLICATION_PATH` and `UPDATE_PATH` (`public/archives`), defaults `APPLICATION_ENV` to `production`, and boots `Zend_Application` with `application/configs/application.ini`.
4. `PG_Controller_Plugin_LayoutPicker` sets the layout name to the module name (`default`). The only layout file is `application/layouts/default.phtml`.
5. Module directory is `application/modules`. The only module is `default`. Routes are Zend defaults: `/{controller}/{action}/{param}/{value}`.

`application/Bootstrap.php` defines `_initDbAdapter` and the method body is empty. The database adapter comes from the ini resource, not from this method.

## Configuration

`application/configs/application.ini` is listed in `.gitignore` and is not in `master`, `qa`, or `prod`. The tracked sample is `application/configs/application.ini.bak`.

Production section of the bak file:

- `display_startup_errors`, `display_errors`, and `displayExceptions` are all `1`.
- Layout `default`. Extra layout keys `admin`, `union`, `referee`, `gir`, `accreditations` are named. Those layout files are not in this repo.
- Locale `af_ZA`.
- Session cookie lifetime `864000`.
- Autoload namespace `PG_`.
- Database adapter `pdo_mysql`, default table adapter.
- Host: `db.footprintapp.net` on `master` and `prod`. `qadb.footprintapp.net` on `qa`.
- Database name: `teamwizz_SARU`.
- Username is present. Password is present and is not reproduced.

`[staging]` inherits production. `[testing]` and `[development]` turn display errors off. Nothing in the repo sets `APPLICATION_ENV` to those names. The default in `index.php` is `production`.

**Discrepancy:** Zend will not boot from a clean checkout or from a pipeline artifact that only contains Git files, because `application.ini` is missing. **Unknown / Requires Confirmation** whether the server keeps a copy that CodeDeploy does not delete.

## Application structure

```text
application/
  Bootstrap.php
  configs/application.ini.bak
  layouts/default.phtml
  models/                    Zend_Db_Table classes
  modules/default/
    Bootstrap.php            empty Default_Bootstrap
    controllers/             Index, Application, Error
    views/scripts/           index, application, error, sean
library/
  Zend/                      Framework 1.11.10
  PG/Controller/Plugin/      layout, mail, images, PDF helpers
  Other/                     Functions.php, Notifications.php
public/
  index.php
  .htaccess
  assets/                    Inspinia theme
  accre/                     local photo/profile assets
  images/
scripts/                     CodeDeploy hooks
appspec.yml
bitbucket-pipelines.yml
codedeploy_deploy.py
```

Controllers that handle product behaviour:

| Controller | Actions used by the portal |
|---|---|
| `IndexController` | `indexAction` (home), `checkemailAction` |
| `ApplicationController` | `apply`, `login`, `loginnow`, `logout`, `profile`, `update`, `updateimage`, `attendance`, `checkemail` |
| `ErrorController` | `errorAction` |

Other actions on `ApplicationController` (`index`, `partners`, `applymultiple`, `applytest`, `testattendance`, `image`, `barcode`, `test`) are described in `features.md`. They are not linked from the layout.

## Data

Models extend `Zend_Db_Table`. Table names below are the `$_name` values or the view names used in `select()->from`.

### Tables this portal writes or reads on the public path

| Name | Used for |
|---|---|
| `AccreditationApplycation` | Application rows. Note the spelling |
| `Accreditation_List` | Joined for `aCode` and name on login and profile |
| `Accreditations_view` | Event profile by `aCode` (`getProfile`) |
| `AccreditationCategory_view` | Public categories |
| `AccreditationRoles_view` | Roles (`Role`, `ARID`, `ACID`, `photo`, `SupplierReg`) |
| `LinkedAccreditations` | Extra events on the Apply step |
| `AccreditationsZones` | Zone names from JSON `aZones` |
| `Accreditation_Applicants_Services` | Services listed on the profile (`getProfileServices`) |

### Other models in the tree, not called by these controllers

`Accreditation`, `AccreditationAdministrators`, `AccreditationApplicationCategory` (the public category query uses the view, the model also has admin helpers), `AccreditationApplicationRole`, `AccreditationCategory`, `AccreditationEventServices`, `AccreditationGates` (`EventGates`), `AccreditationHistory`, `AccreditationLinks` (called), `AccreditationLoadingServices`, `AccreditationLoadingZones`, `AccreditationPermits` (`Accreditation_Permits`), `AccreditationQueing` (`AccriditationApplicationQueing`), `AccreditationServices`, `AccreditationTypes`, `AccreditationZone`.

SQL elsewhere also names views that this portal does not query on the public path: `AccreditationApplycation_view`, `AccriditationWithImage_view`, `Accreditaion_Applicants_Services_view`, `ZonesLIst_view`, `ListAccreditations_view`, `AccriditationNewConfirmedApprovedStatus_view`. Joins mention `tournament`, `Fixture`, `squad`, and `profile`.

There are no migration files. Column lists are implied by insert arrays and view fields. A full schema is **Unknown** without the database.

Application fields written from the form include `FirstName`, `Surname`, `email`, `Mobile`, `IDnumber`, `organisation`, `Category`, `Role`, `Reason_for_pplication`, `User_pic`, `acceptTerms`, `acceptnotes`, `Producer_Fullname`, `Producer_Email`, plus `AC`, `TID`, `FID`, `DateApply`, `Ref`, `History`, `DaysAttending`.

Status integers used by the profile view: `0` with a later `LastUpdate` displays In Review, `1` Confirmed, `2` Declined, `3` Approved. `Pending == 1` overrides those. Provenance: **Implemented** in `profile.phtml`. The columns are not created in this repo.

Queries concatenate request values into SQL (`UserLogin`, `checkEmails`, `getProfile`, `updateData`). Provenance: **Implemented**. This is a review item for anyone still operating the site. No exploit steps are documented here.

## Auth

There is no password hash and no Zend_Auth adapter in the controllers. Login is a lookup of email + reference + accreditation id, then `Zend_Session_Namespace('user_session')` with `AAID` and `Code`.

`updateAction` and `updateimageAction` do not read that session.

`phpinfo.php` exists at the repository root and under `public/`. Provenance: **Implemented**. If those URLs are reachable they disclose PHP configuration.

## Mail

`ApplicationController::sendMail`:

- SMTP host `smtp.gmail.com`, port 587, SSL `tls`, auth login.
- Username `accreditation@magus.events`. Password present in source, not reproduced.
- From the same address, display name `Accreditation - PSL Operations`.
- Subject `PSL Operations Notification`.
- HTML body is the template after placeholder replacement.
- Failure prints the exception.

Commented code shows an older host `mail.footprintapp.net` and from-address `accreditation@footprintapp.net`.

## External systems

| System | Direction | Trigger | Data | Failure |
|---|---|---|---|---|
| MySQL `teamwizz_SARU` on `db.footprintapp.net` (`qadb.footprintapp.net` on `qa`) | App reads and writes | Every page and submit | Accreditation profile, application row, categories, roles, links | Exceptions printed or a PHP error. No retry |
| `https://www.footprintapp.net/accreditations/application/updateimage` | Outbound POST | After a successful insert, and when the profile replaces a photo | Fields `ac`, `ref`, `pic` (data URL). Timeout set to 86400 seconds | `curl` result is not checked. The reference is still returned to the browser |
| `https://www.footprintapp.net/accre/photos/{Ref}.jpg` | Outbound GET (HEAD via `CURLOPT_NOBODY`, then `file_get_contents` if HTTP 200) | Profile view | JPEG bytes turned into a data URL | Missing file falls back to `/accre/profile-icon.png` |
| Gmail SMTP | Outbound | Apply, attendance confirm, attendance cancel | HTML template | Exception printed into the HTTP body |
| AWS CodeDeploy + S3 | Pipeline pushes a zip and creates a deployment | Push to `qa` or `prod` | Whole working tree zip | Script exits 1 on upload or deploy failure. `ignoreApplicationStopFailures` is true |
| `http://supplier.sarugby.online` | Browser link only | Role with `SupplierReg` 1 | None from the server | Not applicable |

No webhooks, queues, or background workers are configured.

## Deployment

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

- Copy `/` to `/mnt/data/www/html/magus.events/psl2020.magus.events/`.
- Owner and group `apache`.
- `BeforeInstall`: `scripts/install_dependencies` (yum install httpd) as root, timeout 300.
- `ApplicationStop`: `scripts/remove_archives.sh` deletes CodeDeploy revision directories that are not the last successful install.
- `ApplicationStart`: `scripts/start_server` (`service httpd start`).

`scripts/stop_server` exists and is not referenced by `appspec.yml`. Its `isExistApp = \`pgrep httpd\`` assignment is not valid Bash (spaces around `=`).

`bitbucket-pipelines.yml` uses image `python:3.5.1`. For branch `qa` or `prod` it exports `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION`, `APPLICATION_NAME`, `DEPLOYMENT_GROUP_NAME`, and `S3_BUCKET` from matching `QA_*` or `PROD_*` pipeline variables, installs zip and `boto3==1.3.0`, zips `*` 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 also reads `DEPLOYMENT_CONFIG`. That variable is not exported in the pipeline file. Provenance: **Implemented**. Effect when the variable is empty is **Unknown / Requires Confirmation**.

The zip includes the working copy. `library/Zend` is in Git, so the framework is deployed with the app. `application.ini` is not in Git, so it is not in the zip.

Commit `76d2dcc` message says the host `psl.magus.events` was added. The filesystem path in `appspec.yml` is `psl2020.magus.events`. The public URL is **Unknown / Requires Confirmation**.

## Front-end notes

- Wizard steps are hidden until `$(function)` shows `#Mainform` (`display: none` on the form).
- Submit uses the jQuery Form plugin `ajaxForm`. The response body is shown as the reference number.
- Cropper aspect ratio is 1.
- Header links: Sign-In `/application/login`. Apply and logout links start hidden and are toggled by the login and profile scripts.
- `default.phtml` function `get_user_browsers()` allows the portal unless the user-agent is empty or matches `MSIE`. Safari is tested before Chrome, so a Chrome user-agent is labelled `safari` and still allowed. See PRD FR-012.
- `Theme == 2` on the accreditation row recolours the wizard steps.

## Development and testing

No README, no local setup doc, and no test suite. `public/index.php` and the controllers turn `display_errors` on. A commented maintenance page sits at the top of `index.php`.

`/application/test` sends a sample mail. `applytest`, `testattendance`, and `barcode` are extra actions. None are a test harness.

## Security notes for operators (no procedures)

These are facts in the tree, listed so a reviewer can decide what to do. They are not a hardening guide.

- Database password and SMTP password are committed (`application.ini.bak`, `sendMail`).
- Production ini sample enables display of errors and exceptions.
- Login and several reads build SQL by concatenating request parameters.
- Profile update and photo update do not check the session.
- `phpinfo.php` is in the tree.
- `random_id` depends on `mcrypt`, which current PHP does not ship.

## Files the task scan mentioned that are not in Git

`sonar-project.properties` and `sonarqube_scan.bat` are not tracked on `master`, `qa`, or `prod`. Provenance: **Unknown**.
