# Technical documentation — indawo

Evidence date: 2026-10-12. Tree: `master` at `ed7defd` (2020-06-03). Full hash `ed7defd4e24b1fbe7157bed2de5a33fa67231691`.

Secret values that appear in committed host files are not copied here.

## Stack

| Item | Evidence | Provenance |
|---|---|---|
| Language | PHP page scripts. Database access is `mysql_connect`, `mysql_query`, `mysql_fetch_object`, `mysql_insert_id` | **Implemented** in `_nemo.database.cls.php` and `system.HTTP.php` |
| PHP version the code can run | The `mysql` extension. It was removed in PHP 7.0. Commit `7692ad1` is titled "Upgrade project to PHP7" and does not replace those calls. `origin/Jacques` is titled "PHP 7.2 upgrade changes" and is not merged | **Implemented** API on `master`. The version a server actually ran is **Unknown / Requires Confirmation** |
| Web model | One PHP file per screen. `index.php` only redirects. No router, no Composer autoload in application code | **Implemented** |
| Online database | MySQL, database `indawo_online` or `sil27_indawo_online` | **Implemented** in the committed host files |
| Back office | Microsoft SQL Server through FreeTDS ODBC strings | **Implemented** in the committed host files and `_nemo.database.SQL_SERVER.cls.php` |
| PDF | `include_once("mpdf/mpdf.php")` from `project.functions.inc.php`. A second tree `mpdf_PHP5.6_version/` is committed. Composer locks `mpdf/mpdf` v8.0.4 and `setasign/fpdi` v2.2.0 under `vendor/` | **Implemented**. Application code does not include `vendor/autoload.php` |
| Spreadsheets | `include_once('PHPExcel/Classes/PHPExcel.php')` | **Implemented** |
| Charts | Highcharts under `plugins/Highcharts/` | **Implemented** |
| HTML to PDF library | `html2pdf/` committed. Not traced to a page include in this pass | **Implemented** as files. Call sites **Unknown** |
| Front-end | Server-rendered tables, `css/`, `js/`, a calendar control under `custom_controls/`, jQuery-style calls inside page scripts | **Implemented** |
| Tests | No project PHPUnit config. PHPUnit files exist only inside third-party packages | **Implemented** absence |
| Package name | `composer.json` has a `require` and no `name` | **Implemented** |

`composer.json`:

```json
{
    "require": {
        "mpdf/mpdf": "^8.0"
    }
}
```

## Application structure

About 4,084 tracked files. Counts from `git ls-tree -r` on `ed7defd`:

| Top | Files | Role |
|---|---|---|
| `PHPExcel/` | 989 | Vendored spreadsheet library, including a `PHPExcel_php5.6_version` subtree |
| `plugins/` | 728 | Highcharts and a Java export server. Some of those Java `target/` paths exceed the Windows MAX_PATH unless Git long paths are enabled |
| `vendor/` | 644 | Composer install of mPDF 8 and its dependencies |
| `rf/` | 343 | Not reviewed file by file |
| `html2pdf/` | 275 | Vendored |
| `mpdf/` | 271 | Legacy mPDF that page code includes |
| `mpdf_PHP5.6_version/` | 263 | Second legacy mPDF |
| `images/` | 222 | UI images |
| repository root | 123 | Page scripts, Nemo classes, crons, pipeline, README |
| `includes/` | 76 | Entity classes and reports |
| `Cash Flow Report/` | 75 | Generated PDF samples |
| `css/`, `js/`, `layouts/`, `charts/`, `orders/`, `RequestInvoice/`, `JobReport/`, `ProfitOverviewPDF/`, `custom_controls/` | small | Layout, charts, and a few generated artefacts |

Nemo classes at the root:

- `_nemo.cls.php`, `_nemo.basic.cls.php` — page shell.
- `_nemo.list.cls.php`, `_nemo.list.basic.cls.php`, `_nemo.details.cls.php` — lists and forms.
- `_nemo.menu.cls.php`, `_nemo.menu.basic.cls.php` — menu from `sysSecurity`.
- `_nemo.database.cls.php` — MySQL via `mysql_*`.
- `_nemo.database2.cls.php`, `_nemo.database.SQL_SERVER.cls.php` — second connection and SQL Server.
- `_nemo.email.cls.php`, `_nemo.password.cls.php`, `_nemo.report.cls.php`, `_nemo.control.cls.php`.
- `_sync.cls.php`, `_sync.php`, `_sync.update.php`.

Layouts: `layouts/header.incl.php`, `layout.menu.incl.php`, `layout.basic.incl.php`, `layout.back.incl.php`, `layout.blank.incl.php`, `layout.iframe.incl.php`.

Bootstrap include used by `index.php`, `_nemo.basic.cls.php`, `ajax.functions.php`, and the menu classes:

```php
if($_SERVER["SERVER_NAME"] == "localhost" )
   include_once("_system.php");
else
   include_once("system.php");
```

`.gitignore` ignores `system.php`, `_system.php`, and `SESSION/*`. Those two PHP files are not in the clone. A comment in `index.php` dated 2019-08-01 says localhost refused a file named `system.php`.

## Host switch (`system.HTTP.php` and `system.HTTPS.php`)

No `include` of either filename was found. They are committed snapshots of a host switch. Whether an operator copies one of them onto the gitignored `system.php` is **Unknown / Requires Confirmation**.

Both files define the same host keys. Differences that were compared: `online.indawo.local` sets `SERVER_PROTOCOL` to `HTTP` in `system.HTTP.php` and to `HTTPS` in `system.HTTPS.php`. Other password and host lines match between the two files on the lines that were read. Usernames and passwords are present in plaintext and are omitted here.

| Host key | MySQL database | SQL Server (FreeTDS) | Session path |
|---|---|---|---|
| `lamp` | `indawo_online` on localhost | `SQL-SERVERR2` port `1433`, database `ind_INDAWO` | `{DOCUMENT_ROOT}/indawo_online/SESSION/` |
| `silicon-overdrive.co.za` (also rewritten from `www.silicon-overdrive.co.za`) | `sil27_indawo_online` on `cmysql5-5.wadns.net:3306`. Comment: WebAfrica | No SQL Server block on the lines read | `{DOCUMENT_ROOT}/indawo_online/SESSION/` |
| `192.168.100.31` | `indawo_online` on localhost. Comment: onsite server | `SQL-SERVERR2` port `1433`, database `ind_INDAWO` | `{DOCUMENT_ROOT}/indawo_online/SESSION/` |
| `online.indawo.local` | `indawo_online` on localhost. Comment: "LIVE LIVE" | `CPTSQL01` port `49834`, database `CLIENTS` | `{DOCUMENT_ROOT}/SESSION/` |

If the requested host has no entry, both files set `SERVER_NAME` to `online.indawo.local` before connecting.

`session.gc_maxlifetime` is `7200`. The MySQL handle is `mysql_connect`. Settings rows from `sysSettings` are loaded into `$SystemSettings` when the database select succeeds.

`SyncRecord::UpdateLocalSyncKey()` treats host name `lamp` as a non-writing path.

## Environment template

There is no `.env` and no dotenv loader. Configuration is the gitignored `system.php` / `_system.php` plus `sysSettings` rows.

`README.md` tells a developer to import `/db/backup.sql` and edit `/application/config/database.php`, then serve vhost `local.indawo`. Those paths are not in the repository. The README also documents AWS CodeDeploy (region `eu-west-1`, application name `CodedeployDev`, S3 bucket `codedeploy`) and FTP variables. The pipeline file does not run CodeDeploy.

## Authentication data

Session object `$_SESSION["ONLINE"]` fields set by `login.php`: `ID`, `USERNAME`, `EMAIL`, `SECURITYGROUPID`, `USERCODE`, `OWCOMPANYID`, `OWCOMPANYNAME`, `OWCOMPANYFOLDERPATH`.

Tables named by the code that was read (not a full schema):

- `sysUser`, `sysSecurityGroup`, `sysSecurity`, `sysMenuLevel1`, `sysMenuLevel2`, `sysSettings`, `sysCompany`, `sysLogin`, `sysPassword` (via the password class).
- `sysSyncMappings`, `sysSyncLog`, `sysOrderCancelLog`.
- `tblOrder`, `tblOrderAuthorisations`, `tblForeman`, `tblJobSuggestions`, `tblContact`, `tblSalesPerson`, `tblJobType`.
- View `vJob` in filter SQL inside `project.functions.inc.php`.

Rights flags on `sysSecurity`: `blnView`, `blnDelete`, `blnSave`, `blnNew`, `blnSpecial`.

## Sync data flow

1. Crons 1 to 6 call the sync classes with an entity name, primary key, and unique column.
2. `sysSyncMappings` supplies local table, remote table, remote database, and field pairs.
3. The SQL Server read lands in a staging table, then replaces MySQL rows whose sync key is not null. The steps are commented in cron 1: disable foreign keys, truncate staging, populate, delete synced rows, insert from staging, run local evals, email the log.
4. The other direction used by suggestion accept builds an INSERT or UPDATE in `SyncRecord` and `Sync::doQuery()` runs it on SQL Server.
5. Companies included in the cost-code pass are active rows with `syncRemoteDatabase` set.

There is no `sysSyncQueue` file or symbol on this `master`.

## Pipeline and analysis

`bitbucket-pipelines.yml` uses image `python:3.5.7`.

| Branch key | What the script does |
|---|---|
| `master` | `apt-get` install `zip`, `git`, `git-ftp`, then `git ftp init` with `FTP_USERNAME_LIVE`, `FTP_PASSWORD_LIVE`, `FTP_HOST_LIVE`. The `git ftp push` line is commented |
| `development` | Same packages, then `git ftp push` with the `*_QA` variables to `ftp://$FTP_HOST_QA/public_html/indawo.overdriveqa.co.za`. The `git ftp init` line for QA is commented |

`origin/dev` points at the same commit as `master`. The YAML key is `development`, not `dev`. `origin/development` is `dfe637c` (2019-09-20), which is not `master`.

No `sonar-project.properties` is committed. No test stage is in the pipeline.

`git ftp init` on every master build is what the YAML says. Whether Bitbucket actually ran it, and whether init-on-every-build was safe, is **Unknown / Requires Confirmation**.

## Security notes (no secret values)

- `system.HTTP.php` and `system.HTTPS.php` commit database usernames and passwords for MySQL and SQL Server. They should be treated as exposed history even though the running bootstrap includes different filenames.
- `phpinfo.php` is a tracked page.
- `login.php` contains a commented block that would set a session without a password. It is commented.
- `CheckSecurityLock` and several list queries interpolate ids into SQL. `NemoDatabase::qs()` exists. Not every query uses it.
- `SyncRecord` `eval`s mapping expressions stored in `sysSyncMappings`.
- Highcharts Java `target/` build output is committed, including `.class` files.
- Sample PDFs and spreadsheets under `Cash Flow Report/`, `Contact Log.xls`, and `results.xls` are business documents in git.
- This document does not repeat passwords, FTP secrets, or the commented session identities.

## Development and testing

README tools: Wamp or Xampp, phpMyAdmin or HeidiSQL, SourceTree, and an editor. **Documented**. Those steps then diverge from this tree, as the project plan records.

No project test suite, fixture database, or CI test command was found. Verification of this onboarding was reading git and the PHP sources. The application was not executed.

Checkout note: a normal `git clone` on Windows failed while creating long paths under `plugins/Highcharts/exporting-server/java/.../target/`. Reading the tree required Git `core.longpaths` for that command. The PHP application files were readable after that.

## Relationship to other Indawo repositories

| Silicon Ark directory | Repository | How it relates to this tree |
|---|---|---|
| `indawo` | `overdrive/indawo` | This document. `master` stopped on 2020-06-03 |
| `indawo_65` | `overdrive/indawo_65` | Later Indawo Online tree. Same page and cron names. Adds MFA and other behaviour this `master` does not have. Ancestry was not proven with a diff |
| `indawo_84` | `overdrive/indawo_84` | Later still, on evidence date 2026-10-12. Not this repository |
| `indawo_docker` | `overdrive/indawo_docker` | Docker Compose for a PHP 5.6 LAMP environment. No application source |
| `indawo_online_docker` | `overdrive/indawo_online_docker` | A `.gitignore` only |

`quantiviaLive` in Silicon Ark has already recorded an "Indawo / Code Deploy" README and an FTP path `public_html/indawo.overdriveqa.co.za`. This repository's README is that same title, and this repository's pipeline pushes to that same QA path. The two READMEs were not diffed. Whether one was copied from the other is **Unknown / Requires Confirmation**.
