# Technical documentation — dev.knowmore (Knowmore)

Evidence date: 2026-10-17.

Source: `git@bitbucket.org:overdrive/dev.knowmore.git`, `master` at `43f9c23` (2021-02-10, author rachelmogano, "Initial Commit"). Only branch: `master`. `origin/HEAD` points at `master`.

The scan-host path `~/projects/sonar_scan/repos/dev.knowmore` was not on the machine that produced this document. Facts below come from that git revision. Password values, payment keys, and personal data are omitted. The files that contain them are named.

## Architecture

PHP pages rendered on each request. No framework package, no Composer project, no front-end build. Shared behaviour is a set of classes named Nemo (`NemoBasic`, `NemoDatabase`, `NemoEmail`, `NemoControl`, `NemoMenu`).

```text
[WordPress / earlier TYPO3 site]          not in this commit
        |  session LEARNER, iframes, about/* pages
        v
training/                                 learner portal
  _nemo.basic.cls.php
  includes webadmin/_system.php           file not in this commit
        |
        v
tools/                                    practice tools
  session.php -> webadmin/system.php      this path is tools/webadmin/system.php
  bat / gap / cat / lit engines
  purchase, registration, cron
        |
        v
tools/webadmin/                           staff UI for tools
  login.php -> missing webadmin/_nemo.password.cls.php
        |
        v
MySQL  knowmore_2016 / knowmore_2016_qa / knowmore_2016qa
        ^
webadmin/includes/                        academy class library, no page controllers
fim_2.0/                                  separate config, different database
```

Page output is HTML built with string concatenation and `include` of layout files. JavaScript is jQuery plus page-level scripts. PDF is produced in-process with FPDF, HTML2FPDF, and FPDI.

## Application structure

| Path | Role |
|---|---|
| `training/` | Learner pages and `NemoBasic` |
| `layouts/` | Header and column layouts included by training pages |
| `tools/` | Advisor tools, commerce, cron, registrations |
| `tools/webadmin/` | Tools admin controllers, `system.php`, Nemo database classes |
| `webadmin/includes/` | Academy entity classes |
| `webadmin/_framework/` | KnowMore file-integrity class |
| `webadmin/import/` | CPD CSV extracts |
| `webadmin/documents/SLA/` | SLA document files |
| `fim_2.0/` | Second file-integrity app, American Shutters config |
| `htmltopdf/`, `tools/htmltopdf/` | Duplicated HTML-to-PDF library |
| `css/`, `js/`, `images/`, `flowplayer/` | Shared assets |
| `tools/upone_fileadmin/` | Local Typo3-style fileadmin config (`localconf.php`) |

The commit contains about 6,600 files. Most are PNG images under `tools/`. About 720 files are PHP. There is no root `index.php`.

Files the training and admin entry points include, and that are absent:

- `webadmin/_system.php`
- `webadmin/_nemo.control.cls.php`
- `webadmin/_nemo.email.cls.php`
- `webadmin/_nemo.password.cls.php`

`getTypoIframe` and `getTypoIframe2` are called from training pages and are not defined in this commit. They are expected to come from the missing system file. **Inferred** from the name and from `_.htaccess` being a TYPO3 sample.

## Languages and libraries

| Piece | Evidence |
|---|---|
| PHP | All application code. Comments dated 2015-07-23 say the tools bootstrap was changed for PHP 5.6. `mysqli_connect` is the live call. Older `mysql_*` calls remain in comments and in some class methods |
| MySQL | `mysqli`. Database names below |
| JavaScript | jQuery 1.4.2 (`tools/js/jquery-1.4.2.js`) and jQuery 1.10.2 (loaded from `tools/header.php`), jQuery UI 1.8.6, Flowplayer 3.2.6, MD5 helper, input masks |
| CSS | `css/styles.css`, `css/Redesign.2014.css`, `css/Redesign.2018.css`, copies under `tools/css` and `training/css` |
| PDF | FPDF, HTML2FPDF, FPDI, `FPDI_Protection` |
| Excel | `tools/Excel/reader.php`, `webadmin/Excel/excel_reader2.php` |
| Mail | `tools/mailer/MAIL5.php` and `NemoEmail` |
| SOAP | `SoapClient` in `tools/soap.php` only |

No automated test directory, phpunit config, or linter config is in the commit.

`sonar-project.properties` and `sonarqube_scan.bat` were named in the onboarding scan and are not in git.

## Environments

Active map: `tools/webadmin/system.php`. Selection key is `$_SERVER[SERVER_NAME]` after two rewrites (`knowmore.co.za` to `www.knowmore.co.za`, `qa-celestis.co.za` to `www.qa-celestis.co.za`). An empty server name becomes `www.knowmore.co.za`.

Three variable groups are filled for each host: `DATABASE_SETTINGS`, `DATABASE_SETTINGS2` (comments still say "academy"), and `DATABASE_SETTINGSBAT1`. On the blocks that are not commented out, all three name the same database. A 2016 comment says the code was changed to one database.

| Host key | MySQL host | Database | Session directory (under document root) |
|---|---|---|---|
| `debian` | `192.168.100.6` | `knowmore_2016_qa` | `/REPO_Knowmore_QA/webadmin/SESSION/` |
| `lamp` | `localhost` | `knowmore_2016_qa` | `/knowmore_qa/webadmin/SESSION/` |
| `dev.overdrive.co.za` | `sql23.cpt1.host-h.net` | `knowmore_2016qa` | `/knowmore/webadmin/SESSION/` |
| `www.qa-celestis.co.za` | `sql22.jnb1.host-h.net` | `knowmore_2016_qa` | `/webadmin/SESSION/` |
| `www.knowmore.co.za` | `sql21.cpt1.host-h.net` | `knowmore_2016` | `/webadmin/SESSION/` |

Commented blocks in the same file name older targets: `celestis` on `sql5.cpt3.host-h.net`, and `celestis_academy` on `sql10.cpt3.host-h.net`. Usernames and passwords for every block, including the commented ones, are plaintext in that file. They are not copied here.

Sage Pay Pay Now service keys are also plaintext in that file, one value for `lamp` and `www.qa-celestis.co.za`, and a different value for `www.knowmore.co.za`. The keys are not copied here.

`fim_2.0/fim.config.php` is a different map: host `sql28.cpt3.host-h.net`, database `american_shutters`, environment `qa`, project name American Shutters. Its password is plaintext and is not copied here.

Other config files that still describe Celestis (`tools/system.inc.LIVE.php`, `tools/xsystem.inc.php`, `tools/webadmin/include/system.php` with database `celestis_tools`) are not the file `tools/session.php` includes. `tools/session.php` includes `webadmin/system.php`, resolved from the `tools/` directory.

After a successful `mysqli_connect`, every row of `sysSettings` is copied into `$SystemSettings` by the `strSetting` column. Landing page, logout redirect, VAT, SMTP, SMS, and `LiveURL` come from that table. The table contents are not in git.

Connection failure prints `Database Connection has Failed!` plus `mysqli_connect_error()`, with type `Critical Error`.

Session gc lifetime is 7200 seconds. `session.save_path` is the host's `sessionpath`.

`display_errors` is turned on, then turned off by `SERVER_NAME != "lamp" || SERVER_NAME != "debian"`. That expression is true for every string, so errors are hidden on every host. `error_reporting` is `E_ALL` without strict, notice, warning, or deprecated.

## Data

There is no schema dump. Table names below are ones the read code addresses. This is not a full catalogue.

| Area | Tables and views seen |
|---|---|
| Settings and security | `sysSettings`, `sysUser`, `sysSecurity`, `sysSecurityGroup`, `sysMenuLevel2`, `sysLogin`, `tblLogin`, `sysSpam` |
| People and practices | `tblLearner`, `tblUser`, `tblUserType`, `tblUserCategory`, `tblPractice`, `tblPracticeDetail`, `vieWPS` |
| Industry | `tblIndustry`, and the practice-type / category / sub-category tables used by the admin screens |
| Catalogue | Course, module, workshop, and forum tables used inside `webadmin/includes` (class files own the SQL) |
| Tools menu | `mnuTier1`, `mnuTier2` (the tier menu in `tools/menu.php` is inside a comment; `tools/header.php` still queries `mnuTier1`) |
| BAT | `tblQQ_2020`, `dwfBatResults_2020`, questionnaire id 4 on the overview, version 1 in `bat.engine.php` |
| Commerce | `tblLicence`, `tblInvoice`, `tblTransaction`, `tblProduct` |
| Jobs | `tblCronJobToDo` (`intStatus` 0 pending, 1 in progress) |
| SLA | `slaQuote` and related service tables inside `sla.quote.cls.php` |
| FIM | `sysAudit_master` and `sysAudit_<environment>` — the `CREATE TABLE` statements in `fim.config.php` are commented out |

Login audit is split. The overview and `tools/login.php` use `sysLogin`. Lockout and the admin login use `tblLogin`. **Requires confirmation** which table production actually has.

CPD import files in `webadmin/import/` are CSV with header `ID`, `Email`, `CPDCategory`, `Event Date`, `PointsFPI`, `PointsFSB`, `FPIRef`, `Business Item`. They contain email addresses. Do not copy them into tickets or logs.

## Integrations

| System | Direction | Trigger | Notes |
|---|---|---|---|
| MySQL on Hetzner `host-h.net` | App to database | Every tools request and cron | Host map above. Failure is a printed critical error |
| WordPress theme `knowmore` | Browser to public site | Logout links, tools logo | Files not in this repo. Logo hard-coded to `https://www.qa-celestis.co.za/wp-content/themes/knowmore/images/logo.png` |
| CMS `hidden/*` iframes | Browser to CMS | Overview, publications, AVP, tools home | Function `getTypoIframe` is not in this repo |
| PayGate PayXML | PayGate POSTs to the app | `tools/payGateResults.php` | MD5 checksum. Secret in source is `test` |
| Netcash | App return, and a SOAP client | `tools/purchase.netcash.php`, `tools/soap.php` | Return script exits before the business steps. SOAP script is a debug client against `https://www.netcash.co.za/ws/wsvalidation/netcash.webservices.validation.asmx?wsdl` |
| Sage Pay Pay Now | Keys present | `SAGEPAY_SETTINGS` in `system.php` | The posting page was not isolated. Keys are committed |
| MyMobileAPI | App POSTs | `SendSMS()` | `http://www.mymobileapi.com/api5/http5.aspx`. Credentials from `sysSettings` |
| SMTP | App sends | `NemoEmail`, cron | From `KnowMore <info@knowmore.co.za>` in tools mail helpers. Cron From and BCC from `sysSettings` |
| Google Analytics and Google Charts | Browser | `NemoBasic::GoogleAnalytics`, pie-chart script | |
| FIM mail | App sends | FIM classes | KnowMore recipients are hard-coded in the framework class. The `fim_2.0` recipients and From address are American Shutters addresses. Neither list is repeated here |

`tools/adfs-idp-hosted.php` is SimpleSAMLphp example metadata (`auth` = `example-userpass`). It is not wired to a login page that this pass found.

## Background work

| Script | Schedule named by the filename | Work |
|---|---|---|
| `tools/cron.daily.php` | Daily, if something calls it | Licence expiry mail, CAT reminders, `ConsultingHours.csv` import |
| `tools/cron.hourly.php` | Hourly, if something calls it | Reset jobs in progress for 90 minutes. Send up to 400 `tblCronJobToDo` rows. Types: `CSS`, `CSS Reminder`, `Renewal`, `Invoice`, `Seminar` |
| `tools/cron.weekly.php` | Weekly, if something calls it | One email of licences inside `+8 weeks` |
| `fim_2.0/fim.cron.php` | Not stated | File-integrity actions, including "FIM: PULL MASTER DATA" |

No crontab, systemd unit, or pipeline file is in the commit. `zcron.daily.php`, `zcron.hourly.php`, and `zcron.weekly.php` match the sizes of the unprefixed scripts.

The hourly file comment says Hetzner cron could run at most every two hours and send at most 500 emails (2012-09-19).

## Security observations

These are properties of the snapshot, recorded so the knowledge base is accurate. They are not an invitation to use them.

- Database passwords and Sage Pay service keys are committed in `tools/webadmin/system.php` and in older include files. `fim_2.0/fim.config.php` commits another project's password.
- Advisor login stores and compares MD5 with no salt visible in `tools/login.php`.
- The posted password is written into `sysLogin` / `tblLogin`.
- `Login2()` copies the password into `$_SESSION[ADVISOR]->PASSWORD` and the cookie `celpassword`.
- `tools/webadmin/login.php` assigns `$btnLogin` inside the condition and then accepts the login when `$btnLogin` is set, so the `NemoPassword` result is not required. The password class file is also missing.
- The five-failure lockout exists and is commented out at the login pages that were read. Its user-facing string still says Celestis. It reads `tblLogin` while the client login writes `sysLogin`.
- `tools/phpinfo.php` calls `phpinfo()`.
- `tools/soap.php` prints SOAP request and response bodies.
- `tools/purchase.netcash.php` dies on a debug print.
- Admin login leaves `$xdb->blnDebug=1`.
- A cron run with no HTTP host uses the live database.
- CPD CSVs with email addresses are in git.
- `webadmin/js/_nemo.fim.cls.php` and `webadmin/images/_nemo.fim.cls.php` are PHP class copies sitting in static directories.

A 2017-11-02 comment on `system.php` says passwords were reset on live and QA after a Hetzner breach. Whether the values still in the file post-date that reset is **Unknown**.

## Development and testing

One commit, message "Initial Commit", author rachelmogano, 2021-02-10. No test suite. No CI config. No README.

Local hosts expected by config are the names `lamp` and `debian`, plus `dev.overdrive.co.za`. QA host in the active map is `www.qa-celestis.co.za`. Live host is `www.knowmore.co.za`.

Duplicated libraries (`htmltopdf`, FPDI, Nemo FIM class, cron scripts, dated class backups) are how this tree keeps history. Git history does not.

## Deployment

**Unknown / Requires Confirmation.** Nothing in the commit describes the document root, the virtual hosts, or the cron user. Session paths imply the Knowmore tree is mounted at the vhost document root, with `training/` and `tools/` as subdirectories, and a sibling WordPress install that provides `second.wp.php`.

The tools header points its logo at the QA host even when the request is served from another host. That is **Implemented** as a hard-coded URL.
