Follow-up to #39. Adds account management, a fuller media library, navigation menus driven by the site scan, a new-page wizard, and deleted-page recovery.
Users (Tools > Users)
Every user can update their display name, email address and password. Administrators can add, edit, invite (one-time link, 7 days), block, unblock and delete accounts.
Accounts in config.local.php stay controlled by config (the main administrator); only a display name and email can be set for them. Accounts created in the CMS live in cms/data/users.json. Sign-in is by login name.
Roles are now editor and administrator.publisher is merged into editor (editors can publish and schedule) and stays accepted as an alias.
Account protection: password hashes only (12 to 72 characters), administrator password confirmation for sensitive actions, rate-limited failures, hashed single-use invite tokens, self-lockout guards, sessions of blocked/deleted/re-passworded users end on their next request, every account event audited without secrets.
New optional dataDirectory setting to keep data outside the web root; the dashboard recommends it while data is inside the web root.
Media library
Upload images, documents, video and audio, sorted into Imagery / Documents / Video / Audio by extension. An editor toolbar button inserts an image, a document link, or a video/audio player.
Safety: extension allow-list (no executables, scripts, HTML/SVG, archives, legacy or macro Office files), content sniffing, refusal of executable signatures and of files containing <?php / <script (short markers in text files only), PDFs with scripts/launch actions refused, Office/OpenDocument packages inspected for macros, embedded objects and scripts (built-in ZIP reader; the zip extension is not needed). Best-effort hardening, not a malware scanner.
uploads/.htaccess (outside cms/) denies more executable/markup extensions and serves documents as downloads.
Navigation, pages and interface
The site scan detects primary/secondary navigation and pre-fills Navigation Menus. Menus can be edited, renamed and deleted, not created by the CMS; a scan button finds navigation added later.
New page wizard (template, name, navigation group) as a section of the Pages dialogue. New pages are private drafts until first published; any page can be used as a template.
Deleted pages can be recovered from the Dashboard.
Full-width top bar with the signed-in user at the top right, device-preview icons, consistent button styles, and a fix for the logo URL in subfolder installs.
Things to review
Behaviour changes: editors can now publish; menus can no longer be created from the CMS; New Page no longer copies a template straight into the site. docs/CHANGELOG.md has migration notes.
Tests:tests/run.php is replaced by the updated suite, because the previous one asserted behaviour that intentionally changed. It covers users and roles, invitations, media type checks, new pages, menus and recovery.
New files: cms/src/NeoCMS/UserStore.php, cms/src/NeoCMS/MediaTypes.php, docs/CHANGELOG.md.
Docs updated: README, ARCHITECTURE, DEVELOPMENT, the welcome page.
Test plan
php tests/run.php passes
php -l on all first-party PHP, node --check cms/js/cms.js, git diff --check
Tried in a browser on XAMPP (Windows), in a subfolder install: users (add, invite and accept, block, delete), media uploads and refusals (.exe, exe renamed .pdf, .php.jpg, JPEG with PHP appended), inserting each media type in the editor, the new-page wizard, and deleted-page recovery
Not tested on Linux/Apache or with a live web server outside XAMPP
## Summary
Follow-up to #39. Adds account management, a fuller media library, navigation menus driven by the site scan, a new-page wizard, and deleted-page recovery.
### Users (Tools > Users)
- Every user can update their display name, email address and password. Administrators can add, edit, invite (one-time link, 7 days), block, unblock and delete accounts.
- Accounts in `config.local.php` stay controlled by config (the main administrator); only a display name and email can be set for them. Accounts created in the CMS live in `cms/data/users.json`. Sign-in is by login name.
- **Roles are now `editor` and `administrator`.** `publisher` is merged into `editor` (editors can publish and schedule) and stays accepted as an alias.
- Account protection: password hashes only (12 to 72 characters), administrator password confirmation for sensitive actions, rate-limited failures, hashed single-use invite tokens, self-lockout guards, sessions of blocked/deleted/re-passworded users end on their next request, every account event audited without secrets.
- New optional `dataDirectory` setting to keep data outside the web root; the dashboard recommends it while data is inside the web root.
### Media library
- Upload images, documents, video and audio, sorted into Imagery / Documents / Video / Audio by extension. An editor toolbar button inserts an image, a document link, or a video/audio player.
- Safety: extension allow-list (no executables, scripts, HTML/SVG, archives, legacy or macro Office files), content sniffing, refusal of executable signatures and of files containing `<?php` / `<script` (short markers in text files only), PDFs with scripts/launch actions refused, Office/OpenDocument packages inspected for macros, embedded objects and scripts (built-in ZIP reader; the `zip` extension is not needed). Best-effort hardening, not a malware scanner.
- `uploads/.htaccess` (outside `cms/`) denies more executable/markup extensions and serves documents as downloads.
### Navigation, pages and interface
- The site scan detects primary/secondary navigation and pre-fills Navigation Menus. Menus can be edited, renamed and deleted, not created by the CMS; a scan button finds navigation added later.
- New page wizard (template, name, navigation group) as a section of the Pages dialogue. New pages are private drafts until first published; any page can be used as a template.
- Deleted pages can be recovered from the Dashboard.
- Full-width top bar with the signed-in user at the top right, device-preview icons, consistent button styles, and a fix for the logo URL in subfolder installs.
## Things to review
- **Behaviour changes:** editors can now publish; menus can no longer be created from the CMS; New Page no longer copies a template straight into the site. `docs/CHANGELOG.md` has migration notes.
- **Tests:** `tests/run.php` is replaced by the updated suite, because the previous one asserted behaviour that intentionally changed. It covers users and roles, invitations, media type checks, new pages, menus and recovery.
- New files: `cms/src/NeoCMS/UserStore.php`, `cms/src/NeoCMS/MediaTypes.php`, `docs/CHANGELOG.md`.
- Docs updated: README, ARCHITECTURE, DEVELOPMENT, the welcome page.
## Test plan
- [x] `php tests/run.php` passes
- [x] `php -l` on all first-party PHP, `node --check cms/js/cms.js`, `git diff --check`
- [x] Tried in a browser on XAMPP (Windows), in a subfolder install: users (add, invite and accept, block, delete), media uploads and refusals (`.exe`, exe renamed `.pdf`, `.php.jpg`, JPEG with PHP appended), inserting each media type in the editor, the new-page wizard, and deleted-page recovery
- [ ] Not tested on Linux/Apache or with a live web server outside XAMPP
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Follow-up to #39. Adds account management, a fuller media library, navigation menus driven by the site scan, a new-page wizard, and deleted-page recovery.
Users (Tools > Users)
config.local.phpstay controlled by config (the main administrator); only a display name and email can be set for them. Accounts created in the CMS live incms/data/users.json. Sign-in is by login name.editorandadministrator.publisheris merged intoeditor(editors can publish and schedule) and stays accepted as an alias.dataDirectorysetting to keep data outside the web root; the dashboard recommends it while data is inside the web root.Media library
<?php/<script(short markers in text files only), PDFs with scripts/launch actions refused, Office/OpenDocument packages inspected for macros, embedded objects and scripts (built-in ZIP reader; thezipextension is not needed). Best-effort hardening, not a malware scanner.uploads/.htaccess(outsidecms/) denies more executable/markup extensions and serves documents as downloads.Navigation, pages and interface
Things to review
docs/CHANGELOG.mdhas migration notes.tests/run.phpis replaced by the updated suite, because the previous one asserted behaviour that intentionally changed. It covers users and roles, invitations, media type checks, new pages, menus and recovery.cms/src/NeoCMS/UserStore.php,cms/src/NeoCMS/MediaTypes.php,docs/CHANGELOG.md.Test plan
php tests/run.phppassesphp -lon all first-party PHP,node --check cms/js/cms.js,git diff --check.exe, exe renamed.pdf,.php.jpg, JPEG with PHP appended), inserting each media type in the editor, the new-page wizard, and deleted-page recovery🤖 Generated with Claude Code