---
name: docs_authoring
description: "How to add a page to the evolution/docs end-user documentation system, and where the permissions docs/logic live"
metadata: 
  node_type: memory
  type: reference
  originSessionId: bc0c41e9-a496-45b9-a31e-0d940754b513
---

End-user docs live in `evolution/docs/`, grouped into section folders (`overview/`, `modules/`, `projects/`, `reporting/`, `api/`, `mobile/`, `admin/`).

**To add a doc page**, copy the template pattern any existing page uses (e.g. `docs/modules/assets.php`):
1. `require __DIR__ . '/../includes/bootstrap.inc.php';` (defines `DOCS_ROOT`, `WEB_ROOT`, `e()`).
2. Set `$page`, `$docTitle`, `$docSubtitle`.
3. Include `head.inc.php`, `sidebar.inc.php`, `header.inc.php` from `DOCS_ROOT . '/includes/'`.
4. Content is Bootstrap 5 markup (cards, tables, `material-symbols-rounded` icons). Bootstrap JS via CDN at the bottom.

**ALWAYS create the page's image directory** (Shane's standing request — so he can drop screenshots in without guessing the path). Convention is a **per-page subfolder**: `docs/<section>/images/<pagename>/` (NOT a flat per-section dir — e.g. `docs/projects/images/runsheets/`, `docs/projects/images/rebilling-invoices/`). Point the page's `docFigure()` helper at that subfolder. Run `mkdir -p docs/<section>/images/<pagename>` when adding a page, and tell Shane the exact folder + the filenames each `docFigure()` call expects.

**Sidebar nav** is data-driven — the `$sections` array now lives in `docs/includes/nav.inc.php` (single source of truth, also feeds the search index in `docs-search.inc.php`); `sidebar.inc.php` just `require`s it and renders. Edit `nav.inc.php` to add a page. Each entry is `['title'=>.., 'items'=>[['label'=>.., 'href'=>..]]]` (each `['title'=>.., 'items'=>[['label'=>.., 'href'=>..]]]`; `href` is relative to docs root, e.g. `/projects/jobs.php`). The loop renders each section as a Bootstrap collapse: sections are **collapsed by default**, multiple can be open, and the section containing the current page auto-expands. Active match is now **exact** (`$currentRel === href`), computed from the request path minus the docs base — not the old `isActive()` substring match. Section headers are `<button class="docs-nav-section h5">` with a rotating caret; styling in `docs/includes/docs.css`. The top logo bar (`.docs-topbar`) also renders from this include (before the `nomenu` early-return).

**Two changelog pages** (both vertical timelines styled after the addressbookedit account-history timeline; both under nav "Documentation"):
- `docs/overview/release-notes.php` — **"What's New (Docs)"**: log of *documentation* additions, data-driven from an inline `$releases` array (newest first; types new/updated/fixed). When you add a doc page, prepend an entry.
- `docs/overview/product-updates.php` — **"Product Updates"**: customer-facing *product* release notes, mirrored from prod `evolution.releasenotes` (`status=1 AND public=1`). Data lives in a GENERATED file `docs/overview/product-updates-data.php`; regenerate with `php docs/overview/_build-product-updates.php` (CLI-only, reads `.env` `sqlClaude*`, connects latin1 to keep raw UTF-8; see [[live_inject_release_notes]] + [[prod_mysql_access]]). Body render trusts authored HTML, else escapes + nl2br/`####`/`**bold**`. Home hero's second button points here.

**Docs search now indexes data-driven pages**: `docs-search.inc.php` reads raw source (doesn't execute pages), so PHP-generated body text was invisible. It now also folds in a companion `*-data.php` file's strings (detected via `include __DIR__ . '/x-data.php'`) and invalidates cache on that file's mtime — this is what makes Product Updates release-note text searchable. Reuse the `foo.php` + `foo-data.php` convention for any future data-driven doc page.

Permission docs: `docs/admin/setting-permissions.php` is the canonical end-user write-up of the staff permission system. The underlying logic: levels (0 Denied / 1 Allowed / 2 Admin) are set in [[evolution_permission_system]] via `staffedit.inc` → `permissions` table → loaded into `$_SESSION` at login by `login2save.php` → checked per-screen. If permission behaviour changes in code, update that doc page rather than recording the detail here.
