# Technical documentation — knowmore_php84 (Knowmore)

Evidence date: 2026-10-11. Source: `git@bitbucket.org:overdrive/knowmore_php84.git`, `master` at `2e5a13d0` (2020-10-19). Unmerged branch notes are marked.

Secrets are not copied into this document. The source files do contain them. Treat those files as sensitive.

## Architecture

The repository is one PHP tree deployed as a website. It is not a framework application with a single front controller for the product.

Three applications share the tree:

1. **WordPress 5.2.3** serves the public site. `index.php` loads `wp-blog-header.php`.
2. **Nemo learner and admin** are page scripts. Each script includes a Nemo class, which includes `webadmin/system.php`, which opens MySQL and starts the session.
3. **Tools** are a third set of page scripts under `tools/`, with their own header, engines, and a nested `tools/webadmin/` that still includes an older `system.inc.php` path.

There is no Composer project for the application, no `package.json` for the Nemo code, and no Dockerfile. WordPress and plugins are vendored. The Knowmore theme has its own `style.css`. `twentynineteen` ships its own `package.json` and is not the product build.

## Application structure

```text
knowmore_php84/
  index.php, wp-*.php          WordPress entry
  wp-admin, wp-includes        WordPress 5.2.3 core
  wp-content/themes/knowmore   Product theme
  wp-content/plugins           Vendored plugins
  wp-content/uploads           Uploads (present in the clone)
  login.php, register*.php     Learner auth and registration
  training/                    Learner portal and a copy of Nemo basic classes
  webadmin/                    Nemo admin, system.php, crons, entity classes
  tools/                       BAT, CAT, gap tools, payments, older admin
  layouts/                     Shared HTML layouts for the learner area
  modules/                     Small include modules (captcha, ads, menus)
  htmltopdf/                   html2fpdf / FPDF
  fim_2.0/                     File integrity cron and config
  flowplayer/                  Video player assets
  bitbucket-pipelines.yml      FTP deploy
```

Nemo class roles on the admin side:

| Class file | Role |
|---|---|
| `_nemo.basic.cls.php` | Base page, controls, layouts |
| `_nemo.cls.php` | Admin page. Requires `$_SESSION[USER]`, loads security, sort, filters, paging, toolbar |
| `_nemo.details.cls.php` | Detail form built from database column metadata |
| `_nemo.list.cls.php`, `_nemo.list.basic.cls.php` | List screens |
| `_nemo.database.cls.php`, `_nemo.database2.cls.php` | Data access. `database2` is the PHP 7.2 update |
| `_nemo.menu.cls.php` | Security-group menu |
| `_nemo.password.cls.php` | Salted passwords and policy settings |
| `_nemo.email.cls.php` | Outbound mail |
| `_nemo.control.cls.php` | Form controls |
| `_nemo.fim.cls.php` | File integrity helper used by admin |
| `includes/*.cls.php` | One class per admin entity |

`training/` has its own `_nemo.basic.cls.php`, `_nemo.details.cls.php`, `_nemo.menu.basic.cls.php`, and `_nemo.survey.cls.php`, and includes `../webadmin/system.php`.

## Languages and frameworks

| Item | Evidence |
|---|---|
| PHP | All product logic. No `composer.json` platform pin |
| PHP 5.6 | Comments dated 2015-07-23 ("Upgrade to be php 5.6 compliant") |
| PHP 7.2 | Commits on 2020-03-09 for `NemoDatabase2`. `cat.class.php` comment "201811 - PHP7 Upgrade" replaces a constructor style |
| PHP 8.x | Not declared on `master`. Unmerged 2026 diffs quote previously bare array keys and declare properties that were previously dynamic. Consistent with PHP 8, not labelled 8.4 in the commits |
| WordPress 5.2.3 | `wp-includes/version.php` |
| JavaScript | jQuery and theme scripts under `js/`, `webadmin/js/`, `tools/js/`. Not a separate SPA |
| CSS | Theme `style.css`, `webadmin/css/`, `tools/css/` |
| SQL | Inline in PHP. MySQL / MariaDB types in the succession DDL (`InnoDB`, `latin1_swedish_ci`) |

`webadmin/system.php` sets `display_errors` to `1` and `error_reporting` to `E_ALL` without `E_STRICT`, `E_NOTICE`, and `E_WARNING`.

The details-class change on the tip commit (`2e5a13d0`) replaces a column default of the string `NULL` with an empty string before the control value is chosen. Provenance: **Implemented** on `master`.

## Databases

Connection is `mysqli_connect` in `webadmin/system.php`, using `$DATABASE_SETTINGS` for the current host. A parallel `$DATABASE_SETTINGS2` array is defined for the same hosts and, in the blocks read, names the same database. Call sites in `webadmin/project.functions.inc.php` open that second connection. It is not documented as a replica.

| Host key | `environment` field | Database name | Server |
|---|---|---|---|
| `localhost` | `dev` | `knowmore_2016` | `localhost` |
| `lamp` | `QA` | `knowmore_2016_qa` | `localhost` |
| `dev.overdrive.co.za` | `QA` | `knowmore_2016qa` | `sql23.cpt1.host-h.net` |
| `www.qa-celestis.co.za` | `QA` | `knowmore_2016_qa` | `sql22.jnb1.host-h.net` |
| `www.knowmore.co.za` | `live` | `knowmore_2016` | `sql21.cpt1.host-h.net` |

Usernames and passwords are in the same file and are not repeated here.

After connect, the code loads every `sysSettings` row into `$SystemSettings`.

Schema on `master` is whatever those databases already contain. Tables named from queries include `tblLearner`, `tblUserType`, `tblUserCategory`, `tblPractice`, `tblCourse`, `tblIndustry`, `sysUser`, `sysSecurityGroup`, `sysSecurity`, `sysMenuLevel1`, `sysMenuLevel2`, `sysLogin`, `sysSettings`, `sysPassword`, `modAssessment`, `modMilestone`, `tblTransaction`, `dwfBatResults_2020`, `tblQQ_2020`. This is not a full table list.

The unmerged branch adds:

- `tblsuccessionservicetype`
- `tblSuccessionService` with a foreign key to the type table and `ON DELETE CASCADE`
- an `ALTER` for `tblVoucher.refSuccessionServiceTypeID`

Charset on that DDL is `latin1_swedish_ci`. `dtLastEdit` defaults to `now()`.

`NemoPassword` can `CREATE TABLE sysPassword` and insert settings if the table is absent, then `die`.

WordPress uses a different configuration in `wp-config.php`: database name `knowmvpnxm_wp_165`, user `root`, empty password, host `localhost`, `WP_HOME` and `WP_SITEURL` set to `https://localhost/OVERDRIVE_PROJECTS/Knowmore`. That does not match the live Nemo host block. Provenance: **Implemented** as the committed file. Whether production WordPress uses another config that is not this commit is **Unknown / Requires Confirmation**.

## Auth

| Area | Mechanism |
|---|---|
| Learners | Email plus `NemoPassword` salt and hash. Session key `LEARNER` |
| Admins | Email plus password check in `webadmin/login.php`, session key `USER`, security group id |
| Advisors | Session key `ADVISOR` required by `tools/index.php`. Where that session is created was not fully traced |
| WordPress | Standard WordPress auth keys and salts in `wp-config.php`. `wp-modal-login` plugin is present |
| reCAPTCHA | Legacy `recaptchalib.php` on register pages |
| Reset | `PASSWORD_Reset_Interval`, `forgot.password.php`, `webadmin/resetPassword.php` |

Session cookie flags (`Secure`, `HttpOnly`, `SameSite`) are not set in `system.php`. `session.gc_maxlifetime` is 7200. `session_start` runs on each include of `system.php`.

## Background work

No queue library (no beanstalk, SQS, or database-queue worker) was found. Work is cron-hit PHP:

| Script | Role |
|---|---|
| `webadmin/cron.daily.php` | Course start, module reminder, CSS reminder, AML, IBC |
| `webadmin/cron.hourly.php` | Imports, invoices, certificates, workshops, CSS, CPD, AML/IBC registration mail |
| `webadmin/cron.fim.php` | FIM |
| `tools/cron.daily.php`, `cron.hourly.php`, `cron.weekly.php` | Tool-side crons |
| `fim_2.0/fim.cron.php` | FIM entry |
| `wp-cron.php` | WordPress cron, present because core is vendored |

`system.php` also runs `exec("find ... chmod")` on `training/dcontent` during normal requests, and chmods the session and SLA directories. That is request-path work, not a queue.

Numbered copies `cron.hourly1.php` through `cron.hourly8.php` are on `master` and removed on the 2026 branches.

## Mail

`NemoEmail` (`webadmin/_nemo.email.cls.php`) is the product mailer. Cron builds HTML and calls it. PHP `mail()` is also called directly, including test sends near the top of `cron.daily.php`.

No SMTP host, SES, or Mailgun config was found in the files sampled. Mail depends on the server's local mailer. **Unknown** which transport production uses.

## Payments

| Provider | Where |
|---|---|
| Sage Pay Pay Now | `$SAGEPAY_SETTINGS[host]->PAY_NOW_SERVICE_KEY` in `system.php`. Training transactions label `purchase: SagePay` |
| PayGate | `tools/payGateResults.php` updates `tblTransaction` |
| Netcash | `tools/purchase.netcash.php`, `tools/unj.netcash.php`, mandate HTML in `tools/dtPDF.cls.php` |

## Documents and media

- PDF: `htmltopdf/` (html2fpdf, FPDF, GIF), `webadmin/fpdi/`, `tools/fpdi/`, invoice and report classes.
- Excel: `tools/Excel/`, `webadmin/Excel/`, `product_excel.php`.
- Video: Flowplayer 3.2.x under `flowplayer/`, HTML5 `<video>`, and YouTube embed URLs in `training/module.php`.
- Learner assignment files: directory setting `DownloadableContentDirLearner`.
- Profile images: `training/profilepictures/`.
- `PDF/` at the repo root holds generated PDFs on `master`. `.gitignore` ignores `/PDF/*`, but many PDFs are already tracked. The unmerged invoice branch deletes a large set of them.

## File integrity

`fim_2.0/` contains `fim.cls.php`, `fim.cron.php`, and config files `fim.config.php`, `fim.config_dev.php`, `fim.config_qa.php`, `fim.config_live.php`. Admin has `_nemo.fim.cls.php` and `cron.fim.php`. Behaviour is permission normalisation (`0755` directories, `0644` files) and integrity tracking. The config files were not quoted here because they may contain environment paths or secrets.

## WordPress

Theme templates cover the usual hierarchy plus `page-latestblogposts.php`, `header-home.php`, and several `old_*` and dated copies.

Plugins are listed in `features.md`. Notable ones: Wordfence and Wordfence Assistant, WordPress SEO, Contact Form 7, Really Simple CAPTCHA, Advanced Custom Fields, Advanced iframe, Slimstat, a social auto-poster, and `disable-xml-rpc`. `xmlrpc.php` is still in the tree because it is WordPress core. Whether the plugin actually disables it depends on WordPress options. **Unknown**.

`.htaccess` Wordfence block denies `.user.ini`. `.user.ini` sets `auto_prepend_file` to `/usr/www/users/knowmvpnxm/wordfence-waf.php`. The live `env_root` in `system.php` is the same `/usr/www/users/knowmvpnxm/` path.

## Deployment

`bitbucket-pipelines.yml`:

- Image: `python:3.5.7` (pipeline image, not the PHP runtime).
- On `master`: install `zip`, `git`, `git-ftp`, then `git ftp push` to `ftp://$FTP_HOST_LIVE/public_html/knowmore.overdriveqa.co.za` with `FTP_USERNAME_LIVE` and `FTP_PASSWORD_LIVE`. An `git ftp init` line for the same URL is commented.
- On `development`: `git ftp init` to the same path with `FTP_*_QA`. The push line is commented. No `development` branch was on the remote at inspection.

There is no test, lint, or security-scan step in the pipeline.

`sonar-project.properties` was not found. A sibling repository `knowmorescan` has a Sonar key. This repository does not.

## Environments

| Name in code | How it is selected |
|---|---|
| dev | HTTP host `localhost` |
| QA | Hosts `lamp`, `dev.overdrive.co.za`, `www.qa-celestis.co.za` |
| live | Host `www.knowmore.co.za`, also the fallback when `SERVER_NAME` is missing |

FTP target hostname is `knowmore.overdriveqa.co.za`, which is not one of the `DATABASE_SETTINGS` keys. How that vhost maps onto the keys above is **Unknown / Requires Confirmation**.

`tools/system.inc.LIVE.php` and `system.inc.DEV.php` are an older connection style (`cMySQL`). They are not included by the current `system.php`. `system.inc.LIVE.php` contains a plaintext password. Do not copy that file into docs or tickets.

## Security observations

These are facts about the tree, for human review. They are not an exploit guide.

- Database passwords, a Pay Now service key, and WordPress salts are committed in PHP. Rotate them if they were ever used, and stop storing them in Git.
- `wp-config.php` in this commit uses user `root` and an empty password, with `WP_DEBUG` and `SCRIPT_DEBUG` false.
- `display_errors` is enabled in `system.php`. Several register and login scripts also enable it.
- `phpinfo.php` exists at the repository root and under `tools/` (`phpinfo.php`, `info.php`).
- Admin and learner queries often interpolate ids into SQL. Some paths use `$xdb->qs()`. Both styles are present.
- `system.php` runs shell `find` and `chmod` on every include.
- Assignment upload sets the temp file mode to `0777` before `move_uploaded_file` (`training/module.php`).
- Session fixation and cookie hardening were not seen in `system.php`. Login calls `session_destroy()` and later `session_start()` on some pages.
- Usage-log queries were changed so the password column is not displayed. `sysLogin` still records usernames and results.
- The pipeline image `python:3.5.7` is long out of support. That is a fact about the YAML, not a claim about the PHP host.

## Development and testing

No PHPUnit configuration, no `tests/` directory for Nemo, and no CI test step were found. WordPress core contains its own test bootstrap files as part of the vendored release. They are not a product suite.

Local development is implied by the `localhost` and `lamp` setting blocks and by the localhost `WP_HOME`. A README with setup steps was not found.

Git history authors on `master` include Jacques, Joshua Gatley-Dewing, rachelmogano, and Jaco Brits. The 2026 branch adds Uriel_Balela.

## Monitoring

No application log library or APM config was found. `webadmin/usage.log.php` and the admin home query read `sysLogin`. Wordfence and Slimstat are present as plugins. `visit_count` is a plugin. Where those write, and whether anyone alerts on them, is **Unknown**.

`cronjoboutput.txt` under `webadmin/` is tracked on `master` and deleted on the 2026 branches.

## Related Silicon Ark folder

`knowmorescan/` documents `git@bitbucket.org:overdrive/knowmorescan.git` at `71190cf` (2022-06-02). That tree is a later export and app snapshot with a static HTML front. This repository is the 2019–2020 full WordPress-plus-Nemo tree, with unmerged 2026 commits. Do not treat the two folders as the same revision.

## Unknown / requires confirmation

- PHP version actually installed on QA and production.
- Whether `successionservive` is deployed anywhere.
- Crontab entries: which of the cron files the host runs.
- WordPress database contents (pages, menus, users) versus the committed `wp-config.php`.
- Whether the second MySQL settings array was meant to be a second database.
- Mail transport.
- Which payment provider is live for which product.
