# NeoCMS ## Version 3.0 Welcome to NeoCMS, the lightweight, database-free content management system. Built with PHP and JavaScript, NeoCMS is designed to add controlled visual editing to an existing static website without changing how public pages are served. Version 3.0 adds filesystem-backed drafts, revision history, role-based permissions, scheduled publishing, page and media management, SEO controls, shared content, navigation menus, responsive previews, accessibility checks, and an operational dashboard. Public content remains ordinary HTML; the database server may continue its well-earned rest. Further technical documentation: * [Architecture](docs/ARCHITECTURE.md) * [Development and testing](docs/DEVELOPMENT.md) ## Features * No Database Required: NeoCMS works without a database, so you don’t have to worry about setup hassles or performance slowdowns. * Easy Content Editing: The TinyMCE visual editor updates marked content regions without requiring authors to edit HTML. Adding `neo-dupe` allows an element and its children to be cloned or removed. * 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: 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: 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. * 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 To install NeoCMS: * Place the `cms/` directory in the document root of the static website. * Place the `uploads/` directory in the same document root. * 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 Tracked defaults live in `cms/config.php`. Deployment credentials and overrides belong in the Git-ignored `cms/config.local.php`, which must return an array. The supplied Apache rules deny direct access to both files. | Setting | Type | Purpose | | --- | --- | --- | | `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 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 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 The class used to identify editable regions is configurable: ```php 'editableClass' => 'editable', ``` Set it to any single valid CSS class name without a leading dot, for example: ```php 'editableClass' => 'cms-content', ``` Your page markup must then use that class: ```html
This region can be edited.
``` Invalid configured values fall back to `editable`. The `neo-dupe` class remains fixed because it marks repeatable blocks, not general editable content. When overriding `editableClass`, update the classes in `cms/templates/` as well so newly created pages are immediately discoverable by the page picker. ### Users and 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' => [ 'admin' => '$2y$10$...', ], 'roles' => [ 'admin' => 'administrator', ], ``` There are two roles: | 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. Audit files rotate at 10 MiB and are retained for 90 days by default. Override `auditMaxFileBytes` and `auditRetentionDays` beneath `security` where local retention obligations differ. ### Password Hash Example Generate a hash on a trusted command line, then place only the resulting hash in `cms/config.local.php`: ```bash php -r 'echo password_hash(trim(fgets(STDIN)), PASSWORD_DEFAULT), PHP_EOL;' ``` There is deliberately no enabled default account. ## Content Features ### Repeatable Blocks Add `neo-dupe` alongside the configured editable class. The editor provides clone-before, clone-after, and delete controls: ```html
Repeatable content
``` ### Shared Content Use `data-neo-shared` to identify a global region: ```html ``` The Shared Content tool updates every HTML page containing the same key. Publishing a page also captures the current value of its shared regions. ### Navigation Menus Menus come from your site's own navigation. The **site scan** finds each page's `