Add Users, media library, navigation menus from the scan, new-page wizard and deleted-page recovery
Users (Tools > Users) - Everyone can update their display name, email and password; administrators can add, edit, invite (one-time link), block and delete accounts. - Accounts in config.local.php stay locked to config (the main administrator); CMS accounts live in data/users.json. Sign-in is by login name. - Roles are now editor and administrator (publisher is an alias of editor). - Account protection: password hashes only (12-72 characters), administrator password confirmation for sensitive actions, rate-limited failures, hashed single-use invite tokens, self-lockout guards, sessions of blocked/deleted users end on the next request, every account event audited without secrets. - dataDirectory can move data outside the web root; the dashboard recommends it. Media library - Upload images, documents, video and audio, sorted into four categories by extension. - Extension allow-list; executables, scripts, HTML/SVG, archives, legacy and macro Office files refused; content sniffing; files containing code refused; PDFs with scripts refused; Office/OpenDocument packages inspected for macros and embedded objects. - Media library button in the editor inserts an image, document link, video or audio. Navigation menus, pages and interface - The site scan detects primary/secondary navigation and seeds Navigation Menus; menus can be renamed, edited and deleted but not created by the CMS; a scan button finds new navigation. - New page wizard (template, name, navigation group) in the Pages dialogue; new pages stay private drafts until published; pages can be used as templates. - Deleted pages can be recovered from the Dashboard. - Full-width top bar with the user at the top right, device icons, consistent buttons. Tests and docs updated (tests/run.php, README, ARCHITECTURE, DEVELOPMENT, CHANGELOG). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
89d64418de
commit
8946915bfe
@@ -23,19 +23,29 @@ Further technical documentation:
|
||||
* Seamless Integration: Simply add the CMS to any existing static website. Just assign a special class to the elements
|
||||
you want to make editable, and you’re all set.
|
||||
* Consistent Layouts: NeoCMS supports page templates to keep your layouts neat and uniform across all pages.
|
||||
* Drafts and Publishing: Editors can save drafts without changing the public page. Publishers can publish immediately or
|
||||
schedule a publication.
|
||||
* Revision History: Every changed or deleted page receives a timestamped filesystem snapshot that publishers can restore.
|
||||
* Page Management: Search, create, duplicate, rename, and delete editable HTML pages, including pages in subdirectories.
|
||||
* Media Library: Browse uploaded images, reuse them, maintain alternative text, see usage counts, and remove files.
|
||||
* Drafts and Publishing: Clicking Save in the editor keeps the change as a private draft without touching the public page.
|
||||
Editors can then publish immediately or schedule a publication.
|
||||
* Revision History: Every changed or deleted page receives a timestamped filesystem snapshot that editors can restore, and
|
||||
deleted pages can be recovered from the Dashboard.
|
||||
* Page Management: Search, duplicate, rename, and delete editable HTML pages, including pages in subdirectories. The New page section of the Pages dialogue is a
|
||||
three-step wizard (template, page name, navigation group); the new page stays a private draft until it is published. Any page can
|
||||
be offered as a template with Create Template.
|
||||
* Media Library: Upload images, documents, video, and audio; they are sorted into Imagery, Documents, Video, and Audio by file
|
||||
extension. Reuse them from the editor's Media library button, maintain alternative text or link text, see usage counts, and
|
||||
remove files. Executables and files containing code are refused.
|
||||
* SEO Settings: Edit the page title, description, canonical URL, Open Graph image, and robots indexing setting.
|
||||
* Shared Content: Mark content with `data-neo-shared="name"` and update matching regions across the site.
|
||||
* Navigation Menus: Build ordered and nested navigation markup and insert it into an editable region.
|
||||
* Responsive Preview: Preview the current page at desktop, tablet, and mobile widths.
|
||||
* Navigation Menus: The site scan finds each page's primary and secondary navigation and pre-fills the menus. Edit, rename, or
|
||||
delete a menu and the change is applied to every page that uses it. Menus are created by the web developer's markup, not by the
|
||||
CMS.
|
||||
* Responsive Preview: Preview the current page at desktop, tablet, and mobile widths using the device icons in the top bar.
|
||||
* Accessibility Checks: Check common authoring problems such as missing alt text, empty links, heading jumps, missing labels,
|
||||
and absent page metadata before publishing.
|
||||
* Roles and Activity: Use editor, publisher, and administrator roles. The dashboard displays drafts, schedules, system
|
||||
problems, and recent activity.
|
||||
* Site Scan: Analyse every managed page and the media library, then make pages editable in one step. The scan
|
||||
finds content areas, images, navigation, broken local references, and missing SEO tags, and adds the markers NeoCMS needs.
|
||||
* Users and Roles: Everyone can update their display name, email address, and password in Tools > Users. Administrators can add,
|
||||
edit, invite, block, and delete accounts. Two roles, Editor and Administrator, are described on that screen.
|
||||
* Activity: The dashboard displays drafts, schedules, deleted pages, system problems, and recent activity.
|
||||
|
||||
## Installation Guide
|
||||
|
||||
@@ -46,8 +56,15 @@ To install NeoCMS:
|
||||
* Copy `cms/config.local.php.example` to `cms/config.local.php`, add password hashes, and assign roles.
|
||||
* Update the elements you want to make editable by adding the configured editable class. It is `editable` by default.
|
||||
* Confirm that PHP may write to the managed HTML files, `cms/data/`, `cms/logs/`, and `uploads/`.
|
||||
* **Live sites:** create a folder outside the web root (for example `/var/neocms-data`), make it writable by PHP, and set
|
||||
`'dataDirectory' => '/var/neocms-data'` in `cms/config.local.php`. It must be an absolute path. Without this, accounts, drafts,
|
||||
and revisions live in `cms/data/`, which is protected only by the supplied `.htaccess`. Administrators see a Dashboard
|
||||
recommendation until the data folder is outside the web root. Audit logs remain in `cms/logs/`, also protected by `.htaccess`.
|
||||
* Visit `/cms/`, log in, and begin editing.
|
||||
|
||||
The site may also live in a subfolder (for example `http://localhost/mysite/`): the URL prefix is detected automatically. See
|
||||
[Subfolder installs](#subfolder-installs).
|
||||
|
||||
That’s it! You’re up and running.
|
||||
|
||||
## Configuration
|
||||
@@ -57,17 +74,21 @@ Tracked defaults live in `cms/config.php`. Deployment credentials and overrides
|
||||
|
||||
| Setting | Type | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `authentication` | array | Maps usernames to `password_hash()` values; plaintext credentials are rejected. |
|
||||
| `roles` | array | Maps usernames to `editor`, `publisher`, or `administrator`. |
|
||||
| `authentication` | array | Maps the main administrator usernames to `password_hash()` values; plaintext credentials are rejected. These accounts are managed only here. |
|
||||
| `roles` | array | Maps those usernames to `editor` or `administrator` (`publisher` is accepted as an alias of `editor`). |
|
||||
| `security` | array | Configures session expiry, login throttling, request limits, storage quotas, and audit-log retention. |
|
||||
| `uploads` | array | Configures image size, dimension, file-count, and aggregate-storage limits. |
|
||||
| `uploads` | array | Configures the per-file size limit (all media types), image dimension, file-count, and aggregate-storage limits. |
|
||||
| `audit` | boolean | Enables daily activity logs under `cms/logs/`. |
|
||||
| `skipWelcomePage` | boolean | Opens the public root instead of the built-in guide after login. |
|
||||
| `showFullUrl` | boolean | Displays the current page path in the toolbar. |
|
||||
| `editableClass` | string | Selects the single CSS class used to identify editable regions. |
|
||||
|
||||
`dataDirectory` is an internal override used by the integration suite and specialised deployments. Normal installations
|
||||
should allow NeoCMS to use `cms/data/`.
|
||||
`dataDirectory` is the absolute path of the folder holding accounts (`users.json`), drafts, revisions, schedules, and menus. It
|
||||
defaults to `cms/data/`; on a live site, point it at a folder outside the web root (see the Installation Guide) so those files
|
||||
can never be served. The folder is created on first use if PHP may create it, and the CLI scheduler uses the same setting.
|
||||
|
||||
`basePath` (URL prefix of the site root, such as `/mysite`) and `siteRoot` (the folder containing `cms/`) are detected
|
||||
automatically and only need setting behind a path-rewriting proxy.
|
||||
|
||||
### Editable Class
|
||||
|
||||
@@ -95,27 +116,52 @@ created pages are immediately discoverable by the page picker.
|
||||
|
||||
### Users and Roles
|
||||
|
||||
Passwords must be generated with `password_hash()` and stored under `authentication` in `cms/config.local.php`. Assign the
|
||||
same username a role under `roles`:
|
||||
There are two kinds of account:
|
||||
|
||||
* **Config accounts** are the main administrators. Generate a hash with `password_hash()` and store it under `authentication` in
|
||||
`cms/config.local.php`, with a role under `roles`. They can be changed only in that file (in Tools > Users they are listed as
|
||||
"Config"; only their display name and email can be set from the CMS).
|
||||
* **CMS accounts** are created in **Tools > Users** and stored in `cms/data/users.json`.
|
||||
|
||||
```php
|
||||
'authentication' => [
|
||||
'author' => '$2y$10$...',
|
||||
'publisher' => '$2y$10$...',
|
||||
'admin' => '$2y$10$...',
|
||||
],
|
||||
'roles' => [
|
||||
'author' => 'editor',
|
||||
'publisher' => 'publisher',
|
||||
'admin' => 'administrator',
|
||||
],
|
||||
```
|
||||
|
||||
* `editor`: edit content, save drafts, and upload media.
|
||||
* `publisher`: editor permissions plus immediate and scheduled publishing and revision restoration.
|
||||
* `administrator`: publisher permissions plus page, media, shared-content, and menu management.
|
||||
There are two roles:
|
||||
|
||||
Users without an explicit valid role receive `editor`, the least powerful role. Account removal, password-hash changes,
|
||||
and role changes affect active sessions on their next request. Sessions expire after 30 minutes of inactivity and 12 hours
|
||||
in total by default.
|
||||
| Role | Can do |
|
||||
| --- | --- |
|
||||
| `editor` | Edit content, save drafts, upload images, publish or schedule pages, and restore revisions or deleted pages. Cannot change the site structure or manage users. |
|
||||
| `administrator` | Everything an editor can, plus creating new pages, duplicate, rename and delete pages, templates, navigation menus, shared content, site scan, deleting media, and managing users. |
|
||||
|
||||
The former `publisher` role is now part of `editor`; existing `publisher` assignments are treated as `editor`. Users without an
|
||||
explicit valid role receive `editor`, the least powerful role.
|
||||
|
||||
#### The Users screen
|
||||
|
||||
* **My account** (every user): see your login name (set by an administrator), change the display name, the email address (needs the
|
||||
current password), and the password (current, new, confirmation; CMS accounts only). You sign in with your login name and
|
||||
password; the email address is contact information only.
|
||||
* **Roles**: a description of each role, with your own marked.
|
||||
* **User accounts** (administrators): add an account with a password, edit an account (name, email, role, optional password
|
||||
reset), invite someone, block or unblock, or delete. Add, edit, invite, block, and delete ask for the administrator's own
|
||||
password. You cannot block, delete, or demote yourself, and config accounts cannot be changed here.
|
||||
* **Invitations** produce a one-time link that is shown once and expires after 7 days. The invitee opens it and chooses their own
|
||||
password. No email is sent by the CMS.
|
||||
|
||||
#### Account protection
|
||||
|
||||
Passwords are 12 to 72 characters and are stored only as hashes. Invite tokens are stored only as hashes and work once. Secrets
|
||||
never appear in API responses or the activity log. Repeated wrong passwords are rate limited, every account event is recorded in
|
||||
the activity feed and audit log, and blocking, deleting, changing a role, or changing a password affects that user's sessions on
|
||||
their next request. `data/.htaccess` denies web access to `users.json`.
|
||||
|
||||
Sessions expire after 30 minutes of inactivity and 12 hours in total by default.
|
||||
|
||||
When HTTPS terminates at a trusted reverse proxy, set `'cookieSecure' => true` beneath `security` in the local override.
|
||||
This keeps the session cookie marked Secure even when PHP receives the proxy's internal HTTP request.
|
||||
@@ -156,7 +202,12 @@ of its shared regions.
|
||||
|
||||
### Navigation Menus
|
||||
|
||||
Menu items use one line each in this format:
|
||||
Menus come from your site's own navigation. The **site scan** finds each page's `<nav>`: a header, banner, or main navigation is
|
||||
treated as `primary`, a footer or legal navigation as `secondary`, and the block is marked in place with
|
||||
`data-neo-menu="name"`. The first page that contains a menu supplies its items, and a saved menu is never overwritten.
|
||||
|
||||
**Tools > Navigation Menus** (main toolbar) lists the menus. Choose **Edit** to change a menu's display name or items, one per
|
||||
line:
|
||||
|
||||
```text
|
||||
Home | /
|
||||
@@ -164,13 +215,82 @@ About | /about.html
|
||||
Team | /team.html | About
|
||||
```
|
||||
|
||||
The optional third value is the label of the parent item. After saving, the generated `<nav>` can be inserted into the
|
||||
currently selected editable region.
|
||||
The optional third value is the label of the parent item. Saving rewrites only the list inside each marked `<nav>` on every
|
||||
page (other bytes, the `<nav>` attributes, and `aria-current="page"` for the current page are handled), and skips pages that
|
||||
would not change. **Delete** removes a menu from the CMS; pages keep their current navigation. **Scan site for new navigation**
|
||||
finds navigation blocks a developer has added since the last scan.
|
||||
|
||||
The CMS does not create menus: new menus are the web developer's markup, discovered by the scan. The menu key cannot be renamed,
|
||||
but the display name can.
|
||||
|
||||
### Media Library
|
||||
|
||||
**Media** uploads and lists files in four categories, decided by the file extension:
|
||||
|
||||
| Category | Accepted extensions |
|
||||
| --- | --- |
|
||||
| Imagery | jpg, jpeg, png, gif, webp |
|
||||
| Documents | pdf, docx, xlsx, pptx, odt, ods, odp, txt, csv |
|
||||
| Video | mp4, m4v, webm, mov, ogv |
|
||||
| Audio | mp3, wav, ogg, oga, m4a, aac, flac |
|
||||
|
||||
Anything not in this list is refused: executables (`.exe`, `.dll`, `.bat`, `.msi`, ...), scripts and server code (`.php`, `.js`, `.sh`, ...),
|
||||
HTML, SVG, XML, archives, the older `.doc`/`.xls`/`.ppt` formats, and macro-enabled Office files. Every upload is also checked
|
||||
by content, not just name: the file type must match its extension; files that start like an executable or contain `<?php`,
|
||||
`<?=`, `<script`, or `<%` anywhere are refused (so a code payload hidden inside an image is caught); PDFs containing
|
||||
JavaScript, launch actions, or embedded files are refused; and Office/OpenDocument files are refused if they contain macros,
|
||||
embedded objects, scripts, or executables. Names such as `report.exe.pdf` are refused too. These checks are best-effort hardening,
|
||||
not a malware scanner, so keep server-side virus scanning where policy requires it.
|
||||
|
||||
Uploaded files are saved under `uploads/` with a generated name (`price-list-3f2a9c1d.pdf`); the original name is kept as the
|
||||
default link text. Every file is limited by `uploads.maxFileBytes` (10 MiB by default), and PHP's `upload_max_filesize` and
|
||||
`post_max_size` must allow at least that. In the editor, use the **Media library** button to insert an image, a document link, a
|
||||
video player, or an audio player at the cursor. Documents are served as downloads. `uploads/.htaccess` denies executable and markup
|
||||
extensions; for Nginx use the rule shown under Web Server Security.
|
||||
|
||||
### Site Scan
|
||||
|
||||
Administrators can open **Tools > Site scan** to analyse the whole site: page and folder counts, pages that can be made
|
||||
editable, images without alternative text, broken local image/CSS/script references, uploaded files that no page uses, and
|
||||
missing title, description, and Open Graph tags. Tick the pages and the kinds of change to make, then apply:
|
||||
|
||||
* **Content areas**: adds the editable class to `<main>` (or `role="main"`), otherwise to top-level `<article>` or
|
||||
text-bearing `<section>` elements. Headers, navigation, footers, asides, and forms are skipped, and `<body>` is never
|
||||
tagged. Pages that already contain an editable region are left alone; pages with no safe container are reported for manual
|
||||
tagging.
|
||||
* **Images**: adds `data-neo-image` to images outside editable regions. In the editor, clicking one opens a dialogue to pick
|
||||
a Media Library image or change its address and alternative text.
|
||||
* **Navigation**: marks each `<nav>` (primary/secondary) with `data-neo-menu` and pre-fills Navigation Menus from it.
|
||||
* **SEO tags**: inserts only the missing title, description, `og:title`, `og:description`, and `og:image` tags, with values
|
||||
taken from the page. Existing tags are never changed, and canonical and robots values are reported but not invented.
|
||||
|
||||
The scan edits page source directly rather than re-parsing it, so nothing else in a file changes, and a revision of every
|
||||
changed page is kept in Revision history.
|
||||
|
||||
### Drafts and Revisions
|
||||
|
||||
Drafts do not alter public HTML. When opening a page with a saved draft, NeoCMS offers to load it. Publishing creates a
|
||||
revision of the previous public file first. Deleting a page also retains its last revision.
|
||||
revision of the previous public file first. Deleting a page also retains its last revision, and **Dashboard > Deleted pages**
|
||||
lists pages whose newest revision is "Before delete" and whose file is gone, with a **Recover** button that recreates the page
|
||||
from that snapshot. Revisions are pruned by the configured retention limits.
|
||||
|
||||
### New Pages and Templates
|
||||
|
||||
The **New page** section (administrators), under the page list in **Pages**, is a three-step wizard: choose a template, give the page a name, then choose a navigation group
|
||||
(or none). The filename is generated from the name (`About Us` becomes `about-us.html`, numbered if taken). The page is saved
|
||||
only as a private draft: no file exists, so it is not visible on the site, until it is first published (Publish Now or a
|
||||
schedule). Publishing creates the file and adds a link to the chosen menu on every page. Unpublished pages appear in Pages as
|
||||
"New - unpublished" and can be discarded.
|
||||
|
||||
Templates are the files in `cms/templates/` plus any page you choose **Create Template** for in the Pages list. A template page
|
||||
is used live (later edits to it show in new pages), and the **Delete** action in the template list deletes a template file or
|
||||
stops using a page as a template (the page is kept).
|
||||
|
||||
### Subfolder Installs
|
||||
|
||||
The site can live in a subfolder such as `http://localhost/neocms/`. The URL prefix (`basePath`) and the folder that contains
|
||||
`cms/` (`siteRoot`) are detected automatically, page links stored by menus are prefixed accordingly, and the session cookie path
|
||||
follows the CMS. Root installs are unchanged.
|
||||
|
||||
NeoCMS stores metadata, drafts, scheduled content, and revisions under `cms/data/`. Back up this directory together with
|
||||
the website. The supplied `.htaccess` denies direct HTTP access on Apache; other web servers should deny `/cms/data/` and
|
||||
@@ -190,14 +310,20 @@ The server timezone controls PHP date handling; the browser converts the selecte
|
||||
|
||||
## Administration Workflow
|
||||
|
||||
1. Open a page from **Pages**.
|
||||
2. Select a region carrying the configured editable class and edit it in TinyMCE.
|
||||
3. Use **Save Draft** to retain private work, or **Publish** to replace the public HTML after creating a revision.
|
||||
4. Use **Schedule** when publication should occur later.
|
||||
5. Check revisions, accessibility findings, SEO metadata, shared content, and menus through the relevant toolbar dialogues.
|
||||
1. Open a page from **Pages** (or create one in the **New page** section under the list).
|
||||
2. Select a region carrying the configured editable class and edit it in TinyMCE. Clicking an image opens the image dialogue.
|
||||
3. In the editor, click **Save Draft** to keep your change private, **Publish Now** to replace the public HTML after
|
||||
creating a revision, or **Schedule Publish** when publication should occur later. **Cancel** discards unapplied edits.
|
||||
4. Check revisions, accessibility findings, SEO metadata, shared content, and menus through the relevant toolbar dialogues.
|
||||
|
||||
The Save button inside the content-editor modal only applies content to the in-browser preview. It does not write a public
|
||||
file. This distinction is intentional and prevents an innocent wording change from making an unscheduled public debut.
|
||||
The top bar shows the current page path on the left; your name and Log out are at the top right, with the toolbar buttons
|
||||
(Dashboard, Pages, Media, SEO, Navigation Menus, Tools) and the desktop/tablet/mobile preview icons beneath. Tools holds
|
||||
Revision history, Accessibility check, Shared content, Site scan, and Users. Dialogue buttons share one style: small
|
||||
secondary buttons for row actions, larger buttons for actions (Save, Next, Apply), and blue for the primary action.
|
||||
|
||||
Save Draft in the content editor (and Apply in the image and SEO dialogues) stores the change as a private draft. It never writes
|
||||
the public file, which only changes on Publish. This is intentional and prevents an innocent wording change from making an
|
||||
unscheduled public debut. The page header shows **Draft:** once a draft is saved, and reopening the page offers to load it.
|
||||
|
||||
## Page Markup Recommendations
|
||||
|
||||
@@ -208,7 +334,7 @@ common authoring issues but is not a complete accessibility audit.
|
||||
## Requirements
|
||||
|
||||
* PHP 8 running on Linux
|
||||
* PHP DOM and fileinfo extensions
|
||||
* PHP DOM and fileinfo extensions (the fileinfo extension is used to check uploads; the zip extension is not needed)
|
||||
* PHP sessions enabled
|
||||
* Write access to `cms/data/`, `cms/logs/`, `uploads/`, and managed HTML pages
|
||||
* A modern browser with JavaScript enabled
|
||||
@@ -225,7 +351,7 @@ Apache users should allow the supplied `.htaccess` rules. For Nginx, add equival
|
||||
location ^~ /cms/data/ { deny all; }
|
||||
location ^~ /cms/logs/ { deny all; }
|
||||
location ~ ^/cms/(config(?:\.local)?\.php|config\.local\.php\.example|publish_scheduled\.php)$ { deny all; }
|
||||
location ~ ^/uploads/.*\.(php[0-9]?|phtml|phar|cgi|pl|py|sh)$ { deny all; }
|
||||
location ~ ^/uploads/.*\.(php[0-9]?|phtml|phar|cgi|pl|py|sh|exe|dll|bat|cmd|com|msi|asp|aspx|jsp|jspx|html?|xhtml|shtml|svg|xml|js|jar|swf)$ { deny all; }
|
||||
```
|
||||
|
||||
Also disable directory listings, prevent PHP source download through server misconfiguration, serve the CMS only over
|
||||
@@ -254,8 +380,9 @@ php tests/run.php
|
||||
```
|
||||
|
||||
It exercises authentication hardening, session revocation, login throttling, iframe sandboxing, path containment, content
|
||||
limits, configurable editable regions, drafts, publication, revisions, page operations, shared content, menus, and
|
||||
scheduled publishing in temporary directories. See [Development and testing](docs/DEVELOPMENT.md) for the complete check
|
||||
limits, configurable editable regions, drafts, publication, revisions, page operations, new-page privacy and templates,
|
||||
shared content, menu detection and propagation, user accounts (roles, invitations, blocking, password confirmation, config
|
||||
accounts), and scheduled publishing in temporary directories. See [Development and testing](docs/DEVELOPMENT.md) for the complete check
|
||||
list used by this repository.
|
||||
|
||||
## Licence
|
||||
|
||||
Reference in New Issue
Block a user