# Technical documentation — wcu_wp

Evidence date: 2026-09-29. Source: `git@bitbucket.org:overdrive/wcu_wp.git` at `ed9b5b6` (`main`, 2024-12-18). Remote has only `main`.

## Architecture

A single PHP application: WordPress 6.7.1 with themes and plugins committed beside core. There is no custom plugin, no REST route registered by project code, no queue, and no background worker beyond WordPress cron (`wp-cron.php`, which runs on request unless the host defines `DISABLE_WP_CRON` in `wp-config.php` — that file is not in git).

```text
Public HTTP
  → web server (not defined in this repo)
      → WordPress front controller (index.php)
          → wp-config.php on the server (gitignored)
          → MySQL
          → active theme
          → active plugins

Bitbucket Pipelines
  → git-ftp → FTP document root
  → SonarQube scan pipe
```

Multisite is suggested by NS Cloner, a blog-ID check in the child theme, and the SportsPress multisite module. It is not proven. WordPress becomes multisite only when `wp-config.php` defines the multisite constants. That file is absent here.

## Repository layout

| Path | What it is |
|---|---|
| `index.php`, `wp-*.php`, `xmlrpc.php` | WordPress entry points |
| `wp-admin/` | WordPress admin |
| `wp-includes/` | WordPress core libraries. Version constants in `version.php` |
| `wp-content/themes/rookie/` | Parent theme, ThemeBoy Rookie 1.5.4 |
| `wp-content/themes/rookie-child/` | Project child theme (4 files) |
| `wp-content/themes/wru-custom/` | Underscores starter renamed WRU Custom |
| `wp-content/themes/twentytwentyfive/` | Default theme bundled with WordPress 6.7 |
| `wp-content/plugins/sportspress-pro/` | SportsPress Pro 2.7.25 |
| `wp-content/plugins/ninja-forms/` | Ninja Forms 3.8.23 |
| `wp-content/plugins/smart-slider-3/` | Smart Slider 3 3.5.1.25 |
| `wp-content/plugins/ns-cloner-site-copier/` | NS Cloner 4.4.7.5 |
| `wp-content/languages/` | `en_ZA` translations |
| `wp-content/uploads/` | Not in git |
| `bitbucket-pipelines.yml` | Deploy and scan |
| `sonar-project.properties` | Sonar project key only |
| `.gitignore` | Excludes config, uploads, dumps, archives |
| `wp-config-sample.php` | Stock WordPress sample. Placeholders only (`database_name_here`). Not the live config |
| `readme.html` | Stock WordPress readme, not a project readme |
| `license.txt` | WordPress GPL |

`wp-content/` has no `mu-plugins` directory and no `uploads` directory in git.

## Languages and frameworks

| Piece | Version in git | Notes |
|---|---|---|
| PHP | Runtime unknown. Minimum 7.2.24 | `$required_php_version` in `wp-includes/version.php`. Smart Slider 3 checks PHP >= 7.0 itself |
| MySQL | Server unknown. Minimum 5.5.5 | `$required_mysql_version` |
| WordPress | 6.7.1 | `$wp_version`. DB revision `$wp_db_version = 58975`. Package `en_ZA` |
| Rookie | 1.5.4 | Tested up to WordPress 5.7 (theme header) |
| Rookie child | 1.0 | No "Tested up to" line |
| WRU Custom | 1.0.0 | Underscores. Tested up to 5.4. PHP >= 5.6 |
| SportsPress Pro | 2.7.25 | Header tested up to 6.1; readme tested up to 6.7 |
| Ninja Forms | 3.8.23 | |
| Smart Slider 3 | 3.5.1.25 | Requires PHP >= 7.0 and WordPress >= 5.0 |
| NS Cloner | 4.4.7.5 | Tested up to WordPress 6.7 (plugin header) |

No root `composer.json` or `package.json`. `wru-custom` has both, for Underscores lint and CSS build. They are not used by the pipeline. `rookie-child` CSS is not generated from Sass.

## Child theme implementation

Files:

- `style.css` — theme header plus the full visual rules. WordPress loads this as the child stylesheet.
- `functions.php` — requires `inc/template-tags.php`; enqueues parent style and `css/custom.css`; enqueues the missing subsite stylesheet; defines `rookie_footer_credit()`.
- `css/custom.css` — two rules (latest-posts image alignment, `.header-inner` gradient).
- `inc/template-tags.php` — copy of Rookie template tags. The header inner wrapper adds `style="background-color:<?= $options['primary'] ?>"`. Parent `template-tags.php` does not set that inline colour.

Enqueue order on the front end:

1. Child `style.css` (WordPress child-theme stylesheet).
2. Parent `style.css` as handle `parent-style` (enqueued on `wp_enqueue_scripts`, so it can end up after the child stylesheet depending on dependencies; `custom.css` depends on `parent-style`).
3. `css/custom.css` as `child-custom-style`, version `1.0`, dependent on `parent-style`.
4. On blog ID 2 only: `css/subsite-2.css` as `subsite-2-style`, dependent on `child-custom-style`. File missing.

`functions.php` uses a short array syntax and a short echo tag. Short echo tags (`<?=`) are always available in supported PHP versions. No other project PHP was added.

Footer: child defines `rookie_footer_credit` before the parent file loads. Parent wraps its definition in `function_exists`, so the child function stands.

## Plugins and how they load

WordPress loads a plugin only when it is active in the database (`active_plugins` or, on multisite, also network-activated plugins). Committing the directory does not activate it.

SportsPress Pro `includes()` scans `includes/` and includes PHP files in each module directory. A module that SportsPress can switch off still has its files on disk; the plugin's own option decides whether its class is constructed. That option is **Unknown**.

SportsPress Pro `deactivate_core()` calls `deactivate_plugins( 'sportspress/sportspress.php' )` when that plugin is active. The free plugin is not a directory in this repo. The core code is the `includes/sportspress` folder inside Pro.

Ninja Forms 3.8.23 bootstraps from `ninja-forms.php`. It checks for table `{prefix}nf3_forms`. Default prefix in the sample config is `wp_`. Live prefix is **Unknown**.

Smart Slider 3 aborts with an admin notice unless PHP >= 7.0, WordPress >= 5.0, and `NONCE_SALT` is defined. `NONCE_SALT` is a standard `wp-config.php` constant. The sample file defines it as a placeholder. The live salt is not in git and must not be copied into documentation.

NS Cloner constants: `NS_CLONER_PRO_PLUGIN` is `ns-cloner-pro/ns-cloner-pro.php`. That plugin is not in the tree. Log directory defaults to the plugin's `logs/` folder.

## Data

WordPress schema for DB revision 58975, plus plugin tables created on activation.

Known plugin table name in code: `{prefix}nf3_forms` (Ninja Forms). SportsPress stores teams, players, events, and staff as custom post types (`sp_team`, `sp_player`, `sp_event`, `sp_staff`, and the other `sp_*` types listed in Team Access), not as a separate schema documented by this project.

Theme options used by the child theme are the `themeboy` option array (Rookie's option name) plus core header image, header text colour, and site title.

User meta used by Team Access: `sp_team` (multiple rows).

Nothing in git is a data migration. There is no `wp-content/upgrade` content of note beyond what core expects at runtime.

Uploads, form uploads, and slider media would live under `wp-content/uploads/` on the server.

## Authentication and users

- Login, passwords, sessions, and application passwords are WordPress core (`wp-login.php`, salts in `wp-config.php`).
- No SSO, OAuth client, or custom login template was added by the child theme.
- SportsPress branding module can replace the login logo when that module is enabled. **Unknown** if it is enabled.
- Team Access adds a team selector on user profile screens for the limited roles. See business rule BR-005.
- No project code implements rate limiting or captcha.

## APIs

No custom REST route or AJAX action is registered by the child theme.

Stock WordPress endpoints are present because core is vendored: REST API under `/wp-json/`, `admin-ajax.php`, `xmlrpc.php`, `wp-cron.php`. Whether the host blocks XML-RPC is **Unknown**.

Ninja Forms, Smart Slider, and SportsPress register their own admin AJAX and REST routes. Those are vendor surfaces. This document does not list them as a project API.

## External integrations

| Integration | Where | Config in git |
|---|---|---|
| FTP deploy | `bitbucket-pipelines.yml` | Host and password are variables `FTP_HOST`, `FTP_USERNAME`, `FTP_PASSWORD` and the `_QA` set |
| SonarQube | pipeline pipe `sonarsource/sonarqube-scan:2.0.1` | `SONAR_HOST_URL`, `SONAR_TOKEN`. Project key in `sonar-project.properties` |
| Google Maps | SportsPress module `sportspress-google-maps` | No key in git |
| Facebook, Twitter | Matching SportsPress modules | No tokens in git |
| ThemeBoy updates | `sportspress-updater` | No licence key in git |
| SendWP | `ninja-forms` includes `includes/Integrations/sendwp.php` | No key in git |
| overdrive.co.za | Hard-coded footer href | Public URL, `http` |

## Infrastructure and deployment

Pipeline file `bitbucket-pipelines.yml`:

- Image: `atlassian/default-image:latest`.
- Clone depth: 1.
- Caches named `sonar` (`~/.sonar`) and `composer` (`~/.composer/cache`). The FTP step requests the composer cache. No `composer install` runs. Nothing at the repo root requires Composer.
- Branches: `main` and `qa`.
- `main` step name: "Build and Test production". Commands: `apt-get update`, `apt-get -qq install zip git git-ftp`, `git ftp init` to `ftp://$FTP_HOST/`, `git ftp push` to the same URL with `--resume`.
- `qa` step uses the same display name. Init is commented (commented URL included `/public_html`). Active command is `git ftp push` to `ftp://$FTP_HOST_QA/` with `--resume`.
- Both branches then run the SonarQube step (`SONAR_HOST_URL`, `SONAR_TOKEN`).

Protocol is `ftp://`, not `sftp://` or `ftps://`, as written in the YAML.

There is no Docker Compose, no Kubernetes manifest, no Ansible playbook, and no server name.

`sonarqube_scan.bat` is not in the commit that was inspected. A directory listing taken when this onboarding task was created included that filename. Treat it as not part of the git tree.

## Environment configuration

Live environment is whatever `wp-config.php` on the server says. The sample file is the WordPress default:

- `DB_NAME`, `DB_USER`, `DB_PASSWORD`, `DB_HOST` placeholders. Sample host is `localhost`.
- `DB_CHARSET` `utf8`. `DB_COLLATE` empty.
- `$table_prefix = 'wp_'`.
- Eight salt constants, all the sample phrase.
- `WP_DEBUG` false.

`.gitignore` ignores `wp-config.php`, `.htaccess`, and `web.config`. Do not commit a filled-in config.

Bitbucket repository variables required by the pipeline are listed in the architecture section. They are not in the YAML as literals.

## Build, development, and testing

- No local development instructions were written for this project.
- Child theme: edit the four files; no build.
- `wru-custom`: `composer install` and `npm install` are described in its Underscores README (`lint:wpcs`, `compile:css`, `bundle`). That theme is a scaffold. The pipeline does not call those scripts.
- Tests: no PHPUnit, no Jest, no Cypress, no TestSprite config in this repository.
- The pipeline does not run a test command.
- SonarQube is analysis only, and it runs after FTP upload. `sonar-project.properties` does not set exclusions, sources, or a quality gate.

## Security notes from the tree

Observations, not a penetration test:

- Database credentials and salts are kept out of git by `.gitignore`. The sample file holds placeholders only.
- FTP and Sonar credentials are referenced as pipeline variables.
- The deploy URL scheme in git is plain `ftp://`.
- Footer credit uses `http://overdrive.co.za/`.
- `xmlrpc.php` is the stock file.
- Child header prints `$options['primary']` into a style attribute without `esc_attr`.
- Whole WordPress core, including admin, is in the git tree and is what FTP uploads.
- No evidence of a WAF, CDN, or object cache in the repository.

## Operations status

Eight commits, all on 2024-12-18, by Jaco Brits (`8d08db4`, `8b3e997`) and Wessels (`wessels@overdrive.co.za`, the other six). No tags. No commits since. Tip message: "Add initialize on git ftp".

## Related documents

- `project-plan.md` — scope, history, discrepancies.
- `prd.md` — requirements and workflows.
- `features.md` — what is present, unused, or missing.
- `business-rules.md` — rules that the code enforces or implies.
