Welcome
Thank you for choosing Prime Core CMS — a lightweight, feature-rich CMS built with a custom PHP MVC framework. Prime Core is designed for developers, agencies, and content creators who need a fast, secure, and easy-to-customize content management platform.
Key Features
Custom MVC Framework
Clean architecture with zero external PHP dependencies. Router, template engine, ORM-like database layer, and more — all built from scratch.
Advanced Security
CSRF protection, two-factor authentication (TOTP), rate limiting, IP blocking, bcrypt hashing, CSP headers, and XSS prevention.
Theme System
Included Starter theme with a flexible template engine supporting layouts, sections, partials, and auto-escaping. 3-level deep navigation menus, layout switcher (Grid/List/Magazine), social sharing, and reading progress bar. Additional theme skeleton (Flavor) ready for custom development.
SEO Optimized
Meta tags, Open Graph, JSON-LD schema, XML sitemap, custom redirects, robots.txt, and clean URLs out of the box.
Media Management
Full-featured media library with drag-and-drop uploads, folder organization, grid/list views, type filtering, bulk operations, and automatic responsive image generation (4 sizes with srcset).
Admin Dashboard
Beautiful admin panel built with Tailwind CSS, featuring analytics charts, quick actions, activity logs, real-time notification system, and comprehensive content management.
File-Based Caching
Page cache and data cache with configurable TTL, atomic writes, and cache statistics. No Redis or Memcached required.
Contact System
Built-in contact page with Google Maps integration, feedback form with CSRF protection, and admin message management panel.
Email System
Zero-dependency SMTP mailer built with raw PHP sockets. TLS/SSL support, HTML email templates, and a send_email() helper for password resets, contact notifications, and newsletters.
Content Versioning
Full revision history for posts and pages with word-level diff comparison (LCS algorithm), side-by-side compare view, and one-click restore. Autosave prevents data loss.
What Is Included
- Complete Prime Core CMS source code
- Frontend theme: Starter (Editorial Magazine style with Ink + Rose Gold color scheme) plus a Flavor skeleton theme for custom development
- Built-in SMTP email system (no external PHP dependencies)
- Real-time notification system with AJAX polling
- Dedicated user profile page for all roles
- Tailwind CSS standalone CLI (no Node.js required)
- Web-based installer with demo content
- This documentation file
- SaaS-style landing page template (
landing.html) — a premium product showcase page with feature breakdowns, pricing tiers, admin/frontend live previews, trust signals, testimonials, FAQ section, and demo credentials card for one-click demo access
Server Requirements
Before installing Prime Core, ensure your server meets the following requirements. The built-in installer will automatically verify these during setup.
PHP Requirements
| Requirement | Minimum | Notes |
|---|---|---|
| PHP Version | 8.0+ | PHP 8.1 or 8.2 recommended for best performance |
Required PHP Extensions
| Extension | Purpose |
|---|---|
pdo | Database abstraction layer |
pdo_mysql | MySQL database driver |
mbstring | Multibyte string handling (UTF-8 support) |
json | JSON encoding/decoding |
fileinfo | MIME type detection for uploads |
gd | Image processing and dimension detection |
curl | HTTP client functionality |
openssl | Encryption and TOTP 2FA support |
zip | Backup creation and extraction |
Database Requirements
| Software | Minimum Version |
|---|---|
| MySQL | 5.7+ |
| MariaDB | 10.3+ |
Web Server Requirements
- Apache with
mod_rewriteenabled (recommended) - Nginx with equivalent URL rewrite rules
Writable Directories
The following directories must be writable by the web server user (typically www-data or apache):
| Directory | Purpose |
|---|---|
config/ | Configuration files are written during installation |
cache/ | File-based page and data cache storage |
storage/ | Session files, logs, and temporary data |
uploads/ | User-uploaded media files |
content/ | Theme files and content assets |
chmod -R 775 config cache storage uploads content and ensure the directories are owned by the web server group.
Installation
Prime Core ships with a web-based installer that guides you through the entire setup process. No command-line access is required.
Quick Start
-
Upload Files
Upload all Prime Core files to your web server's document root (or a subdirectory). You can use FTP, SFTP, or your hosting provider's file manager. Ensure the
.htaccessfile is included (it may be hidden by default). -
Create a Database
Create a new MySQL database and a database user with full privileges on that database. Note down the database name, username, password, and host (usually
127.0.0.1orlocalhost). -
Run the Installer
Navigate to
https://your-domain.com/installin your web browser. The installer will launch automatically if the application has not been installed yet. -
Requirements Check
The installer verifies your PHP version, required extensions, and directory permissions. All items must pass (shown with green checkmarks) before you can proceed. Fix any issues indicated in red and refresh the page.
-
Database Setup
Enter your database credentials:
- Database Host — Usually
127.0.0.1orlocalhost - Database Port — Default:
3306 - Database Name — The database you created in Step 2
- Username — Your database user
- Password — Your database password
- Table Prefix — Default:
pm_(useful for shared databases)
The installer will test the connection before proceeding.
- Database Host — Usually
-
Create Admin Account
Set up your administrator account with a username, email address, password, and display name. Use a strong password — the system enforces a minimum of 8 characters.
-
Site Settings
Configure your site name, URL, and description. The URL should match your actual domain (e.g.,
https://your-domain.com). Do not include a trailing slash. -
Demo Content (Optional)
Optionally install demo content including sample posts, categories, pages, tags, comments, menus, and media. This is recommended for first-time users to see the CMS in action. You can delete all demo content later.
-
Finalize
The installer creates your configuration files, sets the install lock, and provides you with a link to your admin panel. Save your login credentials!
'install_lock' => false in config/app.php and 'installed' => false.
Nginx Configuration
If you are using Nginx instead of Apache, add the following to your server block to enable clean URLs:
server {
listen 80;
server_name your-domain.com;
root /var/www/html;
index index.php;
# Block access to sensitive directories
location ~ ^/(config|core|storage|cache|admin/controllers|admin/views)/ {
deny all;
return 404;
}
# Block dotfiles
location ~ /\. {
deny all;
}
# Block PHP execution in uploads
location ~ ^/uploads/.*\.php$ {
deny all;
}
# Main rewrite rule
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP processing
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Configuration
All configuration is stored in PHP files within the config/ directory. These files are automatically generated during installation, but can be manually edited afterward.
Application Configuration config/app.php
| Key | Type | Default | Description |
|---|---|---|---|
name | string | — | Your site name, displayed in the title and header |
version | string | 1.0.0 | Application version (do not change) |
url | string | — | Full site URL without trailing slash |
base_path | string | / | Base path if installed in a subdirectory |
admin_path | string | — | Randomized admin URL slug for security (e.g., admin-x7k9m) |
timezone | string | UTC | PHP timezone identifier |
debug | bool | false | Enable debug mode (shows errors; never enable in production) |
installed | bool | false | Set to true after successful installation |
install_lock | bool | false | Prevents reinstallation when true |
theme | string | starter | Active frontend theme directory name |
per_page | int | 10 | Items per page in listings |
cache_enabled | bool | true | Enable file-based caching |
cache_ttl | int | 3600 | Cache time-to-live in seconds |
upload_max_size | int | 10485760 | Maximum upload file size in bytes (10 MB) |
allowed_extensions | array | see below | Permitted file upload extensions |
maintenance_mode | bool | false | Enable maintenance mode (blocks frontend) |
maintenance_message | string | — | Message displayed during maintenance |
cookie_consent | bool | true | Show cookie consent banner |
google_analytics | string | — | Google Analytics tracking ID (e.g., G-XXXXXXXXXX) |
encryption_key | string | — | Auto-generated encryption key for security operations |
Allowed File Extensions (Default)
'allowed_extensions' => [
'jpg', 'jpeg', 'png', 'gif', 'webp',
'pdf', 'doc', 'docx', 'xls', 'xlsx',
'zip', 'rar', 'mp4', 'mp3'
]
Database Configuration config/database.php
| Key | Type | Default | Description |
|---|---|---|---|
host | string | 127.0.0.1 | Database server hostname or IP |
port | int | 3306 | Database server port |
database | string | — | Database name |
username | string | — | Database username |
password | string | — | Database password |
charset | string | utf8mb4 | Character set for full Unicode support |
collation | string | utf8mb4_unicode_ci | Collation for sorting and comparison |
prefix | string | pm_ | Table name prefix (useful for shared databases) |
debug | bool | false | Log all SQL queries (development only) |
config/database.php file to a public repository. It contains database credentials. The .htaccess file blocks direct web access to the config directory, but always exercise caution.
File Structure
Below is the complete directory structure of Prime Core. Understanding this structure is essential for customization and theme development.
Database Schema
Prime Core uses 20 database tables (all prefixed with pm_ by default). The schema is automatically created during installation. Below is a complete reference of every table and its columns.
pm_users
Stores administrator, editor, and author accounts.
| Column | Type | Notes |
|---|---|---|
id | INT AUTO_INCREMENT PK | Primary key |
username | VARCHAR(50) UNIQUE | Login username |
email | VARCHAR(255) UNIQUE | Email address |
password | VARCHAR(255) | Bcrypt hash (cost 12) |
display_name | VARCHAR(100) | Public display name |
avatar | VARCHAR(255) NULL | Path to uploaded avatar image |
role | ENUM('admin','editor','author','demo') | User role with hierarchical permissions |
bio | TEXT NULL | Author biography |
two_factor_secret | VARCHAR(255) NULL | TOTP secret key (encrypted) |
two_factor_enabled | TINYINT(1) DEFAULT 0 | Whether 2FA is active |
recovery_codes | TEXT NULL | JSON array of one-time recovery codes |
status | ENUM('active','inactive') | Account status |
last_login | DATETIME NULL | Last successful login timestamp |
created_at | DATETIME | Account creation timestamp |
updated_at | DATETIME | Last update timestamp |
pm_posts
Blog posts and articles with full SEO metadata support.
| Column | Type | Notes |
|---|---|---|
id | INT AUTO_INCREMENT PK | Primary key |
user_id | INT | FK to pm_users |
category_id | INT NULL | FK to pm_categories |
title | VARCHAR(255) | Post title |
slug | VARCHAR(255) UNIQUE | URL-friendly slug |
excerpt | TEXT NULL | Short summary/teaser |
content | LONGTEXT | Full post content (HTML) |
featured_image | VARCHAR(255) NULL | Path to featured image |
show_featured_image | TINYINT(1) DEFAULT 1 | Show featured image on full post page |
status | ENUM('draft','published','archived','scheduled') | Publication status |
is_featured | TINYINT(1) DEFAULT 0 | Featured post flag |
is_sticky | TINYINT(1) DEFAULT 0 | Sticky post flag |
views | INT DEFAULT 0 | View counter |
reading_time | INT DEFAULT 0 | Estimated reading time in minutes |
meta_title | VARCHAR(255) NULL | SEO meta title override |
meta_description | TEXT NULL | SEO meta description override |
og_image | VARCHAR(255) NULL | Open Graph image override |
schema_type | VARCHAR(50) NULL | JSON-LD schema type (Article, BlogPosting, etc.) |
published_at | DATETIME NULL | Publication date |
scheduled_at | DATETIME NULL | Scheduled publication date |
created_at | DATETIME | Record creation |
updated_at | DATETIME | Last modification |
pm_pages
Static pages (About, Contact, etc.) with template selection and SEO fields.
| Column | Type | Notes |
|---|---|---|
id | INT AUTO_INCREMENT PK | Primary key |
user_id | INT | FK to pm_users (author) |
title | VARCHAR(255) | Page title |
slug | VARCHAR(255) UNIQUE | URL slug |
content | LONGTEXT | Page content (HTML) |
excerpt | TEXT NULL | Short summary |
template | VARCHAR(100) NULL | Custom template file name |
featured_image | VARCHAR(255) NULL | Featured image path |
status | ENUM('draft','published','archived') | Publication status |
published_at | DATETIME NULL | Publication date |
sort_order | INT DEFAULT 0 | Display ordering |
meta_title | VARCHAR(255) NULL | SEO title |
meta_description | TEXT NULL | SEO description |
og_image | VARCHAR(255) NULL | Open Graph image |
created_at | DATETIME | Record creation |
updated_at | DATETIME | Last modification |
pm_categories
Hierarchical post categories with parent-child relationships.
| Column | Type | Notes |
|---|---|---|
id | INT AUTO_INCREMENT PK | Primary key |
parent_id | INT NULL | Parent category (self-referencing FK) |
name | VARCHAR(100) | Category name |
slug | VARCHAR(100) UNIQUE | URL slug |
description | TEXT NULL | Category description |
image | VARCHAR(255) NULL | Category image |
meta_title | VARCHAR(255) NULL | SEO title |
meta_description | TEXT NULL | SEO description |
sort_order | INT DEFAULT 0 | Display ordering |
created_at | DATETIME | Record creation |
updated_at | DATETIME | Last modification |
pm_tags & pm_post_tags
Tags provide a flat taxonomy for posts. The pm_post_tags table is a many-to-many junction table.
| Table | Column | Type | Notes |
|---|---|---|---|
| pm_tags | id | INT AUTO_INCREMENT PK | Primary key |
| pm_tags | name | VARCHAR(50) | Tag name |
| pm_tags | slug | VARCHAR(50) UNIQUE | URL slug |
| pm_tags | created_at | DATETIME | Record creation |
| pm_post_tags | post_id | INT | FK to pm_posts (composite PK) |
| pm_post_tags | tag_id | INT | FK to pm_tags (composite PK) |
pm_comments
Post comments with nested replies (parent-child), moderation status, and optional user association.
| Column | Type | Notes |
|---|---|---|
id | INT AUTO_INCREMENT PK | Primary key |
post_id | INT | FK to pm_posts |
parent_id | INT NULL | Parent comment for threading |
user_id | INT NULL | FK to pm_users (if logged in) |
author_name | VARCHAR(100) | Guest commenter name |
author_email | VARCHAR(255) | Guest commenter email |
author_url | VARCHAR(255) NULL | Guest commenter website |
content | TEXT | Comment text |
status | ENUM('pending','approved','spam') | Moderation status |
ip_address | VARCHAR(45) | Commenter IP (IPv4/IPv6) |
user_agent | TEXT NULL | Browser user agent string |
created_at | DATETIME | Comment timestamp |
pm_media
Media library entries tracking uploaded files, dimensions, and folder organization.
| Column | Type | Notes |
|---|---|---|
id | INT AUTO_INCREMENT PK | Primary key |
user_id | INT | FK to pm_users (uploader) |
filename | VARCHAR(255) | Stored filename (may be sanitized) |
original_name | VARCHAR(255) | Original upload filename |
mime_type | VARCHAR(100) | MIME type (e.g., image/jpeg) |
file_size | INT | File size in bytes |
dimensions | VARCHAR(20) NULL | Image dimensions (e.g., 1920x1080) |
alt_text | VARCHAR(255) NULL | Alt text for accessibility |
folder | VARCHAR(100) DEFAULT '' | Organizational folder |
disk_path | VARCHAR(500) | Relative path on disk |
created_at | DATETIME | Upload timestamp |
pm_menus & pm_menu_items
Configurable navigation menus with hierarchical items and multiple location support.
| Table | Column | Type | Notes |
|---|---|---|---|
| pm_menus | id | INT AUTO_INCREMENT PK | Primary key |
| pm_menus | name | VARCHAR(100) | Menu name |
| pm_menus | location | VARCHAR(50) | Display location (header, footer) |
| pm_menus | created_at / updated_at | DATETIME | Timestamps |
| pm_menu_items | id | INT AUTO_INCREMENT PK | Primary key |
| pm_menu_items | menu_id | INT | FK to pm_menus |
| pm_menu_items | parent_id | INT NULL | Parent item for dropdowns |
| pm_menu_items | title | VARCHAR(100) | Display text |
| pm_menu_items | url | VARCHAR(255) | Link URL |
| pm_menu_items | type | ENUM('custom','page','category','post') | Link type |
| pm_menu_items | target | VARCHAR(20) | Link target (_blank, _self) |
| pm_menu_items | sort_order | INT DEFAULT 0 | Display order |
| pm_menu_items | css_class | VARCHAR(100) NULL | Custom CSS classes |
Other Tables
| Table | Purpose | Key Columns |
|---|---|---|
pm_settings |
Key-value settings storage | setting_key (UNIQUE), setting_value, setting_group |
pm_post_revisions |
Post content revision history | post_id (FK), user_id (FK), title, content, revision_note |
pm_page_revisions |
Page content revision history | page_id (FK), user_id (FK), title, content, revision_note |
pm_user_tokens |
Remember-me and password reset tokens | user_id (FK), selector, validator, type ENUM('remember','password_reset'), expires_at |
pm_redirects |
SEO URL redirects (301/302) | from_url, to_url, status_code, hits |
pm_activity_logs |
Admin activity audit trail | user_id, action, description, ip_address |
pm_newsletter_subscribers |
Email newsletter subscriptions | email (UNIQUE), status, subscribed_at, unsubscribed_at |
pm_login_attempts |
Login attempt tracking for rate limiting | ip_address, username, attempted_at, successful |
pm_blocked_ips |
IP address blocking | ip_address (UNIQUE), reason, blocked_at, expires_at |
pm_contact_messages |
Contact form submissions | name, email, subject, message, ip_address, is_read |
Routing
All routing is defined in the main index.php entry point. Prime Core uses a custom Router class that supports GET, POST, and ANY methods, route groups with prefixes and middleware, named routes, and URL generation.
Frontend Routes
| Method | URI Pattern | Handler | Description |
|---|---|---|---|
| GET | / | FrontendController::home | Homepage with featured/sticky/recent posts |
| GET | /search | FrontendController::search | Search results page (?q=keyword) |
| GET | /page/{slug} | FrontendController::page | Static page display |
| GET | /tag/{slug} | FrontendController::tag | Tag archive page |
| GET | /sitemap.xml | SitemapGenerator | XML sitemap for search engines |
| GET | /robots.txt | Static file | Crawler directives |
| GET | /{slug} | FrontendController::categoryOrPage | Category archive or static page (auto-detected) |
| GET | /{category}/{post} | FrontendController::post | Single post display |
API Routes
| Method | URI Pattern | Description | Response |
|---|---|---|---|
| GET | /api/search?q= | Search autocomplete | JSON array of matching posts |
| POST | /api/comments | Submit a comment | JSON success/error |
| POST | /api/newsletter/subscribe | Newsletter signup | JSON success/error |
| POST | /api/contact | Contact form submission | JSON success/error |
Admin Routes
All admin routes are grouped under the randomized admin path (e.g., /admin-x7k9m/) and protected by authentication middleware. The admin path is set in config/app.php as admin_path.
| URI Pattern | Controller | Actions |
|---|---|---|
/ | DashboardController | index |
/posts | PostController | index, create, store, edit, update, delete |
/pages | PageController | index, create, store, edit, update, delete |
/categories | CategoryController | index, create, store, edit, update, delete |
/tags | TagController | index, store, delete |
/comments | CommentController | index, approve, spam, delete |
/media | MediaController | index, upload, update, delete |
/menus | MenuController | index, create, store, edit, update, delete |
/users | UserController | index, create, store, edit, update, delete |
/profile | ProfileController | index, update (all roles) |
/notifications/poll | — | JSON endpoint for notification polling |
/settings | SettingsController | index, update |
/seo | SeoController | index, redirects (CRUD), generate sitemap |
/newsletter | NewsletterController | index, delete, export |
/contact | ContactController | index, view, delete |
/search | SearchController | Global admin search |
/system-logs | LogController | index, clear |
/cache | CacheController | index, clear |
/backup | BackupController | index, create, download, delete |
Route Middleware
Admin routes use an authentication middleware that checks for an active session and valid user. If not authenticated, users are redirected to the login page. The middleware also verifies the user's status is active.
// Middleware is applied via route groups
$router->group(['prefix' => $adminPath, 'middleware' => 'auth'], function($router) {
$router->get('/', [DashboardController::class, 'index']);
// ... more routes
});
Core Classes
Prime Core is built on a set of foundational classes located in the core/ directory. These form the framework layer that the application is built upon.
Database core/Database.php
A singleton PDO wrapper providing a fluent interface for database operations. Uses prepared statements exclusively to prevent SQL injection.
Key Methods
| Method | Description |
|---|---|
getInstance() | Returns the singleton database instance |
query($sql, $params) | Execute a raw SQL query with parameter binding |
fetch($sql, $params) | Fetch a single row as an associative array |
fetchAll($sql, $params) | Fetch all rows as an array of associative arrays |
insert($table, $data) | Insert a row and return the last insert ID |
update($table, $data, $where, $params) | Update rows matching a WHERE clause |
delete($table, $where, $params) | Delete rows matching a WHERE clause |
count($table, $where, $params) | Count rows in a table |
beginTransaction() | Start a database transaction |
commit() / rollback() | Commit or roll back a transaction |
getPrefix() | Returns the configured table prefix (e.g., pm_) |
// Usage example
$db = Database::getInstance();
$prefix = $db->getPrefix();
// Fetch a single post
$post = $db->fetch(
"SELECT * FROM {$prefix}posts WHERE slug = :slug AND status = 'published'",
['slug' => $slug]
);
// Insert a new record (table name without prefix — prefix is added automatically)
$id = $db->insert("posts", [
'title' => 'My Post',
'slug' => 'my-post',
'content' => '<p>Hello world</p>',
'status' => 'published',
'user_id' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
]);
Router core/Router.php
A lightweight URL router supporting dynamic parameters, route groups, middleware, and named routes.
Key Methods
| Method | Description |
|---|---|
get($uri, $handler) | Register a GET route |
post($uri, $handler) | Register a POST route |
any($uri, $handler) | Register a route for all HTTP methods |
group($options, $callback) | Group routes with shared prefix/middleware |
dispatch() | Match the current request and execute the handler |
url($name, $params) | Generate a URL from a named route |
// Route with dynamic parameter
$router->get('/post/{slug}', [PostController::class, 'show']);
// Route group with prefix and middleware
$router->group(['prefix' => '/admin', 'middleware' => 'auth'], function($router) {
$router->get('/dashboard', [DashboardController::class, 'index']);
$router->post('/posts/store', [PostController::class, 'store']);
});
Template core/Template.php
A PHP-based template engine supporting layouts, sections, partials, and automatic HTML escaping.
Key Methods
| Method | Description |
|---|---|
render($template, $data) | Render a template with given data |
extend($layout) | Specify a parent layout |
section($name) / endSection() | Define a content section |
yield($name) | Output a section's content in the layout |
partial($name, $data) | Include a partial template |
e($value) | HTML-escape a value (alias for htmlspecialchars) |
Template paths use context prefixes:
theme::— Resolves to the active theme's templates directoryadmin::— Resolves to the admin views directory
Session core/Session.php
Secure session management with fixation prevention, timeout enforcement, and flash messages.
Features
- Automatic session ID regeneration to prevent fixation attacks
- Configurable session timeout (default: 30 minutes of inactivity)
- Flash messages (data available only for the next request)
- Remember-me token storage (file-based)
- Secure cookie settings (HttpOnly, SameSite, Secure when HTTPS)
Security core/Security.php
Comprehensive security utilities including CSRF protection, rate limiting, 2FA, and more.
Key Features
| Feature | Details |
|---|---|
| CSRF Protection | Token generation with lifetime-based expiry (default 1 hour). Validated on all POST requests. Tokens stored in $_SESSION['_prime_csrf']. Tokens are not rotated after validation to support multiple AJAX requests per page. |
| XSS Prevention | HTML escaping via htmlspecialchars() with ENT_QUOTES | ENT_SUBSTITUTE and UTF-8 encoding. |
| Password Hashing | Bcrypt with cost factor 12 via password_hash(). |
| Rate Limiting | Database-backed rate limiting on login attempts. Configurable threshold and lockout duration. |
| IP Blocking | Permanent or time-based IP blocks stored in pm_blocked_ips table. |
| File Upload Validation | Extension whitelist, MIME type verification, file size limits, filename sanitization. |
| 2FA (TOTP) | RFC 6238 compliant Time-based One-Time Passwords. Compatible with Google Authenticator, Authy, etc. |
| Security Headers | Content-Security-Policy, X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, Referrer-Policy. |
Auth core/Auth.php
Authentication and authorization manager handling login, session management, and role-based access control.
Role Hierarchy
| Role | Level | Permissions |
|---|---|---|
admin | Highest | Full access to all features, user management, settings, backups |
editor | Middle | Manage all content (posts, pages, categories, tags, comments, media) |
author | Low | Create and manage own posts only |
demo | Lowest | Read-only access to entire admin panel, all POST requests blocked |
Key Methods
login($username, $password)— Authenticate a user with credentialslogout()— Destroy the session and remove remember-me cookiescheck()— Check if a user is currently authenticateduser()— Get the currently authenticated user's datahasRole($role)— Check if the current user has a specific role (or higher)loginFromRememberCookie()— Authenticate via remember-me cookie (selector:validator pattern)generatePasswordResetToken($email)— Create a time-limited password reset tokenverifyTwoFactor($code)— Validate a TOTP code during 2FA verification
Cache core/Cache.php
File-based caching system with two cache types: data cache (serialized PHP objects) and page cache (full HTML output).
Key Methods
| Method | Description |
|---|---|
get($key) | Retrieve a cached value (returns null if expired) |
set($key, $value, $ttl) | Store a value with TTL (uses atomic writes) |
delete($key) | Remove a specific cache entry |
flush() | Clear all cache entries |
getPageCache($url) | Retrieve cached HTML for a URL |
setPageCache($url, $html, $ttl) | Cache full HTML output for a URL |
getStats() | Return cache statistics (size, file count) |
Mailer core/Mailer.php
A zero-dependency SMTP mailer using raw PHP sockets. Supports TLS/SSL encryption, SMTP authentication, HTML emails, and custom headers.
Key Methods
| Method | Description |
|---|---|
send($to, $subject, $body) | Send an HTML email via SMTP |
setFrom($email, $name) | Set the sender address and name |
setReplyTo($email) | Set the reply-to address |
Configuration is loaded automatically from the pm_settings table (keys: smtp_host, smtp_port, smtp_encryption, smtp_username, smtp_password, mail_from_email, mail_from_name).
SitemapGenerator core/SitemapGenerator.php
Generates dynamic XML sitemaps including published posts, categories, and pages with last-modified dates. The sitemap is accessible at /sitemap.xml and is automatically regenerated when content changes.
Helper Functions
Prime Core provides a rich set of global helper functions defined in core/Helpers.php. These are available everywhere in the application.
URL Helpers
| Function | Description | Example |
|---|---|---|
site_url($path) | Generate a full site URL | site_url('/about') → https://example.com/about |
admin_url($path) | Generate an admin panel URL | admin_url('/posts') → https://example.com/admin-x7k9m/posts |
asset_url($path) | Generate a theme asset URL | asset_url('css/theme.css') |
admin_asset_url($path) | Generate an admin asset URL | admin_asset_url('admin.css') |
upload_url($path) | Generate an uploads URL | upload_url('2024/photo.jpg') |
current_url() | Get the current page URL | — |
Text & Formatting Helpers
| Function | Description |
|---|---|
e($value) | HTML-escape a string (XSS prevention) |
truncate_text($text, $length, $suffix) | Truncate text to a specified length with a suffix |
slugify($string) | Convert a string to a URL-safe slug |
format_date($date, $format) | Format a date string |
time_ago($date) | Convert a date to relative time (e.g., "2 hours ago") |
reading_time($content) | Estimate reading time in minutes from HTML content |
format_size($bytes) | Format bytes to human-readable size (KB, MB, GB) |
gravatar($email, $size) | Generate a Gravatar URL from an email address |
generate_toc($content) | Generate a table of contents from HTML headings |
add_heading_ids($content) | Add ID attributes to HTML headings for anchor links |
Security & Session Helpers
| Function | Description |
|---|---|
csrf_field() | Output a hidden CSRF token input field |
csrf_token() | Get the current CSRF token value |
set_flash($key, $value) | Set a flash message (available for one request) |
get_flash($key) | Retrieve and remove a flash message |
has_flash($key) | Check if a flash message exists |
redirect($url) | Redirect to a URL and exit |
get_client_ip() | Get the client's IP address (uses REMOTE_ADDR only; does not trust proxy headers) |
Utility Helpers
| Function | Description |
|---|---|
config($key, $default) | Read a configuration value with dot notation |
json_response($data, $code) | Send a JSON response with HTTP status code |
is_ajax() | Check if the current request is an AJAX request |
is_installed() | Check if the application has been installed |
log_activity($action, $desc) | Log an admin activity to the audit trail |
log_system($message) | Write a message to the system log file |
send_email($to, $subject, $body) | Send an email via the configured SMTP mailer |
Admin Panel
The admin panel is a full-featured content management interface built with Tailwind CSS (production build via standalone CLI — no CDN or Node.js required), TinyMCE 6.8.3 for rich text editing (lazy-loaded on demand), and Chart.js 4.4.1 for analytics charts (lazy-loaded on demand).
Accessing the Admin Panel
The admin panel URL is randomized during installation for security. You can find it in config/app.php under the admin_path key. The full URL follows this pattern:
https://your-domain.com/{admin_path}/
# Example: https://your-domain.com/admin-x7k9m/
Dashboard
The dashboard provides an at-a-glance overview of your site with:
- Statistics cards — Total posts, pages, comments, and users
- Views chart — Post view trends over the past 30 days (Chart.js, lazy-loaded on demand)
- Recent posts — Latest posts with quick-edit links
- Recent comments — Latest comments awaiting moderation
- Activity log — Recent admin actions
- Quick actions — Clear cache, create new post, etc.
Authentication Features
- Login — Username/email and password with rate limiting (5 attempts per 15 minutes per IP)
- Remember Me — Persistent login using secure selector:validator token pattern
- Forgot Password — Email-based password reset with time-limited tokens
- Two-Factor Authentication — TOTP-based 2FA with QR code setup and recovery codes
- Session Security — Auto-timeout after 30 minutes, session fixation prevention
Notification System
The admin panel includes a real-time notification bell icon in the top navigation bar. Notifications are polled every 60 seconds via AJAX and alert administrators to items requiring attention.
Notification Types
- Pending Comments — Comments awaiting moderation approval
- Unread Messages — New contact form submissions not yet read
- New Subscribers — Newsletter signups within the last 24 hours
- Scheduled Posts — Posts scheduled to publish within the next hour
How It Works
- Notification counts are queried at page load from the database and rendered server-side in
main.phplayout - A JavaScript polling loop in
admin.jscallsGET /{admin-path}/notifications/pollevery 60 seconds - The endpoint returns a JSON response with
{ total, items[] } - The bell icon badge updates dynamically — it hides automatically when there are zero notifications
- Clicking the bell opens a dropdown with color-coded notification rows and count badges
- Each notification row links to its relevant admin section (comments, contact, newsletter, posts)
Posts
Posts are the primary content type in Prime Core. They support rich text editing, categories, tags, featured images, SEO metadata, and revision history.
Creating a Post
- Navigate to Posts in the admin sidebar
- Click Create New Post
- Fill in the post details:
- Title — The post headline (slug is auto-generated)
- Content — Rich text editor (TinyMCE) with image uploads, code blocks, tables, etc.
- Excerpt — Short summary for listings and search results
- Category — Select from existing categories (auto-assigned to "Uncategorized" if none selected)
- Tags — Assign multiple tags
- Featured Image — Select from the media library
- Show on full post page — If unchecked, the featured image will only appear in post cards/listings, not on the full post page
- Status — Draft, Published, Scheduled, or Archived
- Featured/Sticky — Flag as featured or sticky post
- Configure SEO settings (optional):
- Meta title and description overrides
- Open Graph image
- Schema type (Article, BlogPosting, NewsArticle)
- Click Save or Publish
Post Statuses
| Status | Behavior |
|---|---|
draft | Not visible on the frontend. Only accessible in admin. |
published | Visible on the frontend. Appears in listings, feeds, and search. |
scheduled | Will be automatically published at the scheduled date/time. |
archived | Hidden from listings but may still be accessible via direct URL. |
Revisions
Every time a post is updated, a revision is automatically saved to the pm_post_revisions table. Revisions store the title, content, user who made the change, and an optional revision note. This provides a complete content audit trail.
Revision Diff & Compare
The post editor includes a revisions sidebar that lists all previous versions. For each revision you can:
- Compare — Opens a side-by-side diff modal showing word-level changes between the revision and the current version, using an LCS (Longest Common Subsequence) algorithm with color-coded additions (green) and deletions (red)
- Restore — Reverts the post content to the selected revision
Autosave
The post editor automatically saves drafts at regular intervals via AJAX, preventing data loss. The autosave status indicator shows when the last save occurred. Use PrimeAdmin.autoSave() to configure autosave behavior.
Auto-Categorization
If a post is saved without selecting a category, it is automatically assigned to an "Uncategorized" category. This category is created automatically if it doesn't exist. This ensures all posts have a valid category for proper URL routing (posts use the /{category}/{slug} URL pattern).
Menu Item Cleanup
When you delete a Category, Tag, or Page, any menu items linking to that content are automatically removed (including child menu items). This prevents broken links in your navigation menus.
Pages
Pages are static content (About, Contact, Terms, etc.) that are not associated with categories or tags. They support custom templates, sort ordering, and full SEO metadata.
Creating a Page
- Navigate to Pages in the admin sidebar
- Click Create New Page
- Fill in the page details:
- Title — The page title
- Content — Rich text editor
- Excerpt — Short summary
- Template — Select a custom template file (optional)
- Featured Image — Optional header image
- Status — Draft, Published, or Archived
- Sort Order — Numeric value for ordering in navigation
- Configure SEO settings (optional)
- Click Save
Page URLs
Pages are accessible at /page/{slug} or /{slug}. The router first checks for a matching category when a single-segment URL is accessed, then falls back to pages.
Custom Templates
You can assign a custom template to any page. Create a new PHP file in your theme's templates/ directory and select it from the Template dropdown when editing the page. For example, a contact.php template is included for the contact page.
Page Revisions
Like posts, pages also support a full revision history system. Every update creates a revision in the pm_page_revisions table. The page editor includes a revisions sidebar with word-level diff comparison and one-click restore — identical to the post revision system.
Categories & Tags
Categories
Categories provide a hierarchical taxonomy for organizing posts. Each category can have:
- Name and Slug — Display name and URL-friendly identifier
- Parent Category — For creating nested hierarchies
- Description — Category description for archive pages
- Image — Category header/thumbnail image
- SEO Fields — Meta title and description for the archive page
- Sort Order — Control display ordering
Category archive pages display all published posts within that category, accessible at /{category-slug}.
Tags
Tags provide a flat (non-hierarchical) taxonomy for cross-cutting content classification. A post can have multiple tags, and tags can be shared across posts.
- Tags are managed inline from the admin panel (create and delete)
- Tags are assigned to posts via the post editor
- Tag archive pages are accessible at
/tag/{tag-slug} - Tags display with post counts in the admin listing
Media Manager
The media manager provides a centralized library for uploading, organizing, and managing files.
Features
- Drag and Drop — Drop files directly onto the upload area
- File Validation — Extension whitelist, MIME type check, and size limit enforcement
- Image Dimensions — Automatic detection and storage of image dimensions
- Responsive Image Variants — Automatic generation of 4 image sizes on upload: Original, Thumbnail (150×150), Medium (max 600×400), and Large (max 1200×800) with responsive
srcsetattributes on the frontend - Folder Organization — Organize files into folders. Folders are scanned from the
uploads/media/directory on the filesystem, so empty folders are also displayed. Create new folders directly from the media manager sidebar. - Alt Text — Inline AJAX editing of accessibility alt text with debounced auto-save
- Grid/List Toggle — Switch between grid view (thumbnails) and list view (table), with preference persisted in localStorage
- Type Filtering — Filter by All, Images, or Documents using tab navigation
- Bulk Actions — Select multiple files with checkboxes and delete in bulk
- Thumbnail Optimization — Grid view displays optimized thumbnails instead of full-size images
- TinyMCE Integration — Browse and insert media directly into posts and pages from the editor
Upload Limits
| Setting | Default | Configuration |
|---|---|---|
| Maximum file size | 10 MB | upload_max_size in config/app.php |
| Allowed extensions | jpg, jpeg, png, gif, webp, pdf, doc, docx, xls, xlsx, zip, rar, mp4, mp3 | allowed_extensions in config/app.php |
upload_max_filesize and post_max_size directives in php.ini must also be large enough to handle your desired upload size. The CMS validates against the lower of the two limits.
Comments
Prime Core includes a comment system with moderation, threaded replies, and spam management.
Comment Flow
- Visitors submit comments via the AJAX-powered comment form on post pages
- Comments are submitted to
/api/commentswith CSRF protection - New comments default to
pendingstatus and require admin approval - Admins can approve, mark as spam, or delete comments from the admin panel
- Only
approvedcomments are displayed on the frontend
Comment Features
- Threaded Replies — Comments support parent-child relationships for threaded discussions
- Guest Comments — Visitors can comment with name, email, and optional URL
- IP Tracking — Commenter IP and user agent are stored for moderation
- Gravatar Support — Commenter avatars are generated from email addresses via Gravatar
- Bulk Moderation — Approve or delete multiple comments at once
Users & Roles
User management supports creating multiple admin, editor, author, and demo accounts with role-based permissions.
Role Permissions
| Feature | Admin | Editor | Author | Demo |
|---|---|---|---|---|
| Dashboard | Full stats | Limited stats | Own stats | View only |
| Posts | All | All | Own only | View only |
| Pages | All | All | — | View only |
| Categories & Tags | Full | Full | — | View only |
| Comments | Full | Full | — | View only |
| Media | Full | Full | Own only | View only |
| Menus | Full | — | — | View only |
| Users | Full | — | — | View only |
| Settings | Full | — | — | View only |
| SEO & Redirects | Full | — | — | View only |
| Backups | Full | — | — | View only |
| Profile | Own | Own | Own | View only |
Profile Management
Prime Core includes a dedicated profile page accessible to all authenticated users (admin, editor, author, and demo) at /{admin-path}/profile. This is handled by ProfileController.php — a separate controller from UserController — ensuring every role can manage their own account without needing access to the Users admin section.
- Display name and bio
- Email address
- Password change (with current password confirmation)
- Avatar upload (with Gravatar fallback)
- Two-factor authentication setup
- Username, role, and account status displayed as read-only
The profile link is available in the top-right user dropdown menu of the admin layout.
Demo Mode
Prime Core includes a built-in demo mode designed for live preview environments (e.g., Envato marketplace demos). When enabled, a special demo user role provides full read-only access to the entire admin panel without allowing any modifications.
How It Works
- Router-Level Middleware — A middleware in
index.phpintercepts all POST requests for users with thedemorole. Only logout is permitted. - AJAX Requests — Returns a JSON
403response with a descriptive message, so AJAX forms display clean error feedback. - Regular POST — Redirects back with a flash message: "This is a demo admin panel. Modifications are not permitted in demo mode."
- Demo Banner — A sticky banner is displayed at the top of every admin page when logged in as a demo user.
- Account Protection — The demo user account cannot be modified or deleted by any user, including admins.
Demo User Setup
A demo user is automatically created when demo content is installed during the installation wizard:
| Field | Value |
|---|---|
demo@example.com | |
| Password | demo |
| Role | demo |
Login Page Integration
When a demo user exists in the database, the login page automatically displays an "Access Demo Panel" button that pre-fills the demo credentials and submits the form — providing a one-click demo experience for visitors.
Settings
The Settings panel provides a centralized interface for configuring all aspects of your site. Settings are stored in the pm_settings table as key-value pairs with grouping.
General Settings
- Site Name — Displayed in the header, title tags, and email templates
- Site Description — Default meta description for the homepage
- Site Logo — Upload a logo image
- Favicon — Upload a favicon
- Timezone — Set the server timezone for date display
- Posts Per Page — Number of posts shown in listings
Social Links
Configure social media profile URLs that are displayed in the frontend sidebar and footer:
- Facebook, Twitter/X, Instagram, LinkedIn, YouTube, GitHub
Analytics & Tracking
- Google Analytics — Enter your GA tracking ID (e.g.,
G-XXXXXXXXXX) - Cookie Consent — Enable/disable the cookie consent banner
Maintenance Mode
Enable maintenance mode to take the frontend offline while performing updates. Logged-in administrators can still access the site. Configure a custom maintenance message.
Contact Page Settings
Contact page settings are displayed when a page with the slug contact exists and is published. The settings tab will show a warning if the contact page is missing or not published, with a quick link to create/edit it.
- Office Address — Physical address displayed on the contact page
- General Email — Primary email address displayed; also used as the Reply-To address for auto-replies
- Support Email — Support email address displayed
- Phone Number — Phone number displayed on the contact page
- Business Hours — Weekday, Saturday, and Sunday hours
- Google Maps Embed URL — Embed URL for the Google Maps iframe on the contact page
Auto-Reply
When enabled, users who submit the contact form receive an automatic confirmation email. This is optional but recommended for professional communication.
- Enable Auto-Reply — Toggle to send confirmation emails automatically
- Email Subject — Subject line for the auto-reply email
- Email Message — Customizable message body with variable support
- Test Button — Send a test email to verify your configuration
Available Variables:
| Variable | Replaced With |
|---|---|
{name} | Submitter's name |
{email} | Submitter's email address |
{subject} | Subject line from the form |
{message} | Message content (truncated to 200 characters) |
{site_name} | Your site name from General settings |
Reply-To Header: Auto-reply emails include a Reply-To header set to your General Email, so users can reply directly to the confirmation email and reach your team.
Default Message:
Hi {name},
Thank you for reaching out to us! We have received your message and our team will get back to you within 24-48 hours.
Best regards,
{site_name}
Email / SMTP Settings
Configure the built-in SMTP mailer for sending emails (password resets, contact notifications, newsletter). See the Email System section for detailed configuration instructions.
- SMTP Host — Mail server hostname
- SMTP Port — Connection port (587 for TLS, 465 for SSL)
- SMTP Encryption — TLS, SSL, or none
- SMTP Username/Password — Authentication credentials
- From Email / From Name — Default sender identity
- Test Email — Send a test message to verify configuration
SEO & Redirects
Built-in SEO Features
- Meta Tags — Custom meta title and description for every post, page, and category
- Open Graph — Full OG tag support for social media sharing (og:title, og:description, og:image, og:type)
- JSON-LD Schema — Structured data markup for posts (Article, BlogPosting, NewsArticle)
- XML Sitemap — Auto-generated sitemap at
/sitemap.xmlincluding posts, categories, and pages with last-modified dates - Robots.txt — Configurable via
/robots.txt - Canonical URLs — Automatic canonical URL tags on all pages
- Clean URLs — SEO-friendly URL structure (
/{category}/{post-slug}) - Breadcrumbs — Automatic breadcrumb navigation
URL Redirects
Manage 301 (permanent) and 302 (temporary) redirects from the SEO settings page. This is useful when you change post slugs or restructure your site. Each redirect tracks the number of hits.
| Field | Description |
|---|---|
From URL | The old URL path (e.g., /old-post-slug) |
To URL | The new URL path (e.g., /new-post-slug) |
Status Code | 301 (permanent) or 302 (temporary) |
Hits | Number of times the redirect has been followed |
Sitemap Generation
The sitemap is generated dynamically and includes:
- All published posts with their last modified date and category URL prefix
- All categories with post counts
- All published pages
- The homepage
You can regenerate the sitemap manually from the SEO settings page, or it is regenerated automatically when content changes.
Newsletter
Prime Core includes a built-in newsletter subscription system for collecting email addresses from your visitors.
How It Works
- A subscription form is displayed in the frontend sidebar
- Visitors enter their email and submit via AJAX (
POST /api/newsletter/subscribe) - The email is validated and stored in
pm_newsletter_subscribers - Duplicate emails are rejected with a friendly message
Admin Management
- View Subscribers — See all subscribers with their status and subscription date
- Delete Subscribers — Remove individual subscribers
- Export to CSV — Download all subscribers as a CSV file for use with email marketing services (Mailchimp, SendGrid, etc.)
- Send Emails — With the built-in SMTP system configured (see Email System), you can send emails directly to subscribers
core/Mailer.php) that can send emails directly. Configure your SMTP settings in Settings → Email to enable email sending for newsletters, contact form notifications, and password reset emails. You can also export subscribers as CSV for use with external email marketing platforms.
Email System
Prime Core includes a zero-dependency SMTP mailer (core/Mailer.php) that sends emails directly from the server using raw PHP sockets. No external libraries like PHPMailer or SwiftMailer are needed.
Configuration
Configure email settings from Settings → Email in the admin panel:
| Setting | Description | Example |
|---|---|---|
SMTP Host | Mail server hostname | smtp.gmail.com |
SMTP Port | Connection port | 587 (TLS) or 465 (SSL) |
SMTP Encryption | Encryption method | tls, ssl, or none |
SMTP Username | Authentication username | your@gmail.com |
SMTP Password | Authentication password | App-specific password |
From Email | Sender email address | noreply@yoursite.com |
From Name | Sender display name | Prime Core |
Features
- Zero Dependencies — Pure PHP socket-based SMTP communication (no Composer packages)
- TLS/SSL Support — Secure connections via STARTTLS or direct SSL
- HTML Email — Sends rich HTML emails with proper MIME headers
- Email Templates — Consistent, branded email templates for all outgoing messages
- Test Email — Send a test email from the admin settings to verify your SMTP configuration
Usage
Use the send_email() helper function anywhere in the application:
// Send an email using the helper
send_email(
'recipient@example.com',
'Subject Line',
'<p>HTML email body content</p>'
);
// Or use the Mailer class directly
$mailer = new \Core\Mailer();
$mailer->send('recipient@example.com', 'Subject', $htmlBody);
Where Emails Are Used
- Password Reset — Sends a reset link to the user's email
- Contact Form — Notifies the admin when a new contact message is received
- Newsletter — Ability to send emails to newsletter subscribers
Contact Messages
The contact system provides a public-facing contact page with a feedback form and an admin panel for managing received messages.
Contact Page Features
- Google Maps Embed — Configurable Google Maps iframe showing your location
- Contact Information — Displays email, phone, and address from settings
- Feedback Form — Name, email, subject, and message fields with CSRF protection
- AJAX Submission — Form submits via
POST /api/contactwith real-time validation feedback - IP Tracking — Sender IP is recorded for spam prevention
Admin Management
- Message List — View all messages with read/unread status
- View Message — Read full message details including sender info and IP
- Mark as Read — Messages are automatically marked as read when viewed
- Delete — Remove messages individually
- Unread Counter — Dashboard and sidebar show unread message count
Configuration
Configure the contact page from Settings in the admin panel:
- Enable or disable the contact page
- Set the contact email, phone, and address
- Configure the Google Maps embed URL (get it from Google Maps > Share > Embed a map)
Theme System
Prime Core uses a theme-based frontend system. Themes are self-contained packages that include controllers, templates, partials, and assets.
Included Themes
| Theme | Description |
|---|---|
| Starter | A clean, minimal theme perfect as a starting point for custom development. Includes all template files. |
| Flavor | A skeleton theme with the directory structure ready for custom development. Copy and build your own design on top of it. |
Design System
Prime Core uses a unified design system across the frontend theme, admin panel, and installer. The visual identity is defined by custom CSS properties and Tailwind config tokens:
| Element | Value |
|---|---|
| Primary Color (Ink) | #0f172a — deep navy |
| Ink Light | #1e293b |
| Ink Muted | #475569 |
| Accent (Rose Gold) | #c2727d |
| Rose Gold Light | #e8a5ad |
| Rose Gold Dark | #a85d67 |
| Background (Cream) | #fafaf9 |
| Warm Gray | #f1f5f9 |
| Heading Font | Outfit (geometric sans-serif, weights 400–800) |
| Body Font | Plus Jakarta Sans (sans-serif, weights 300–700) |
| Monospace Font | Fira Code (weights 400–500) |
These values are defined as CSS custom properties in theme.css (frontend), admin.css (admin panel), and inline styles in install/view.php (installer). The Tailwind configs (tailwind.frontend.config.js and tailwind.admin.config.js) mirror these tokens for utility class generation.
Theme Structure
Switching Themes
To switch the active theme, change the theme value in config/app.php:
// config/app.php 'theme' => 'flavor', // Change from 'starter' to 'flavor'
Creating a Custom Theme
- Copy the
startertheme directory and rename it - Modify the templates, CSS, and JavaScript to match your design
- Update the
FrontendController.phpif you need to change data fetching logic - Update
config/app.phpto use your new theme name
FrontendController.php in each theme handles all frontend data fetching and template rendering. It provides data to templates and receives route parameters from the router. Study the Starter theme's controller to understand the data flow.
Template Reference
Every frontend template receives a set of common variables plus template-specific data. All variables are accessible as PHP variables within templates.
Common Variables (All Templates)
| Variable | Type | Description |
|---|---|---|
$siteName | string | Site name from settings |
$siteDesc | string | Site description |
$siteLogo | string|null | Logo image URL |
$favicon | string|null | Favicon URL |
$gaCode | string|null | Google Analytics tracking ID |
$headerMenu | array | Header navigation menu items |
$footerMenu | array | Footer navigation menu items |
$settings | array | All site settings as key-value pairs |
$socialLinks | array | Social media URLs |
$cookieConsent | bool | Whether to show the cookie banner |
$sidebar | array | Sidebar data (categories, tags, recent posts) |
$breadcrumbs | array | Breadcrumb navigation trail |
$pageTitle | string | Page title for the <title> tag |
$metaDesc | string | Meta description for the page |
$canonical | string | Canonical URL |
$ogType | string | Open Graph type (website, article) |
$ogImage | string|null | Open Graph image URL |
$templateFile | string | Current template filename |
Template-Specific Variables
home.php
$featuredPosts— Array of featured posts$stickyPosts— Array of sticky posts$posts— Paginated array of recent posts$pagination— Pagination data (current page, total pages, etc.)
localStorage so it survives page reloads. The switcher UI is rendered as icon buttons above the post grid.
post.php
$post— The current post object (all columns)$category— The post's category$tags— Array of tags associated with the post$comments— Approved comments (threaded)$relatedPosts— Related posts from the same category$author— Author information$toc— Table of contents generated from headings$prevPost— Previous post (for navigation, with thumbnail)$nextPost— Next post (for navigation, with thumbnail)
category.php
$category— The current category object$posts— Paginated posts in this category$pagination— Pagination data
tag.php
$tag— The current tag object$posts— Paginated posts with this tag$pagination— Pagination data
search.php
$query— The search query string$posts— Matching posts$totalResults— Total number of results$pagination— Pagination data
contact.php
$contactEmail— Contact email from settings$contactPhone— Contact phone from settings$contactAddress— Contact address from settings$googleMapsUrl— Google Maps embed URL from settings
page.php
$page— The current page object (all columns)
API Endpoints
Prime Core provides four public API endpoints used by the frontend JavaScript for dynamic interactions. All endpoints return JSON responses.
Search Autocomplete
GET /api/search?q={query}
// Response (200 OK)
[
{
"title": "Post Title",
"url": "/category-slug/post-slug",
"excerpt": "Short excerpt...",
"image": "/uploads/featured.jpg"
}
]
Submit Comment
POST /api/comments
Content-Type: application/x-www-form-urlencoded
Parameters:
post_id (required) - The post ID
author_name (required) - Commenter name
author_email (required) - Commenter email
content (required) - Comment text
parent_id (optional) - Parent comment ID for replies
csrf_token (required) - CSRF token
// Response (200 OK)
{
"success": true,
"message": "Comment submitted for moderation"
}
// Response (422 Validation Error)
{
"success": false,
"message": "Validation error description"
}
Newsletter Subscribe
POST /api/newsletter/subscribe
Content-Type: application/x-www-form-urlencoded
Parameters:
email (required) - Subscriber email
csrf_token (required) - CSRF token
// Response (200 OK)
{
"success": true,
"message": "Successfully subscribed!"
}
// Response (409 Conflict)
{
"success": false,
"message": "Email already subscribed"
}
Contact Form
POST /api/contact
Content-Type: application/x-www-form-urlencoded
Parameters:
name (required) - Sender name
email (required) - Sender email
subject (required) - Message subject
message (required) - Message content
csrf_token (required) - CSRF token
// Response (200 OK)
{
"success": true,
"message": "Message sent successfully!"
}
// Response (422 Validation Error)
{
"success": false,
"message": "Validation error description"
}
Security
Prime Core implements multiple layers of security to protect your site and its data.
CSRF Protection
All POST requests are protected by CSRF tokens. The system generates a unique token stored in $_SESSION['_prime_csrf'] and validates it on every form submission.
<!-- In forms, use the csrf_field() helper -->
<form method="POST">
<?= csrf_field() ?>
<!-- form fields -->
</form>
<!-- For AJAX requests, include the token as a parameter -->
<script>
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
fetch('/api/endpoint', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'csrf_token=' + csrfToken + '&other=data'
});
</script>
Two-Factor Authentication (2FA)
Users can enable TOTP-based two-factor authentication from their profile settings.
- Enable 2FA from the user profile page
- Scan the QR code with an authenticator app (Google Authenticator, Authy, etc.)
- Enter the 6-digit verification code to confirm setup
- Save the recovery codes in a secure location
When 2FA is enabled, users must enter a 6-digit code after their password on every login. Recovery codes provide backup access if the authenticator device is lost.
Rate Limiting
Login attempts are rate-limited per IP address. After 5 failed attempts within 15 minutes, the IP is temporarily blocked from further login attempts. All attempts (successful and failed) are logged in the pm_login_attempts table.
IP Blocking
Administrators can manually block IP addresses from the Security settings. Blocked IPs are denied access to the entire site. Blocks can be permanent or time-limited (with an expiration date).
Content Security Policy (CSP)
Prime Core sends a Content-Security-Policy header that restricts resource loading to trusted sources:
- Scripts: Self, CDN hosts (cdn.tiny.cloud, cdn.jsdelivr.net) — TinyMCE and Chart.js are loaded from CDN on demand
- Styles: Self, with
unsafe-inlinefor TinyMCE compatibility - Images: Self, data URIs, blob URIs, Gravatar, uploads
- Fonts: Self
- Frames: Self, Google Maps (google.com/maps)
Security Headers
The following security headers are set via .htaccess and PHP:
| Header | Value |
|---|---|
X-Content-Type-Options | nosniff |
X-Frame-Options | SAMEORIGIN |
X-XSS-Protection | 1; mode=block |
Referrer-Policy | strict-origin-when-cross-origin |
Directory Protection
The .htaccess file blocks direct web access to sensitive directories and files:
config/— Configuration files with database credentialscore/— Framework source codestorage/— Sessions, logs, and temporary filescache/— Cached data and pagesadmin/controllers/— Admin controller source codeadmin/views/— Admin template source code.env,.git— Environment files and git data
Additionally, PHP execution is blocked in the uploads/ directory to prevent uploaded script execution.
Randomized Admin URL
During installation, the admin panel URL is assigned a random slug (e.g., /admin-x7k9m/) instead of a predictable /admin/. This prevents automated bots from targeting your login page.
Caching
Prime Core includes a file-based caching system that requires no external services like Redis or Memcached. Caching significantly improves page load times by avoiding redundant database queries.
Cache Types
| Type | Storage | Purpose |
|---|---|---|
| Data Cache | cache/data/ | Serialized PHP objects (query results, computed data) |
| Page Cache | cache/pages/ | Full HTML output of rendered pages |
Configuration
// config/app.php 'cache_enabled' => true, // Enable/disable caching globally 'cache_ttl' => 3600, // Cache lifetime: 3600 seconds (1 hour)
Cache Management
From the admin panel, navigate to Cache to:
- View cache statistics (total files, total size)
- Clear all cache with a single click
- Cache is automatically invalidated when content is created, updated, or deleted
Atomic Writes
The caching system uses atomic file writes (write to temp file, then rename) to prevent data corruption from concurrent access. This ensures cache files are never in a half-written state.
Customization
Custom Theme Development
The recommended approach for customization is creating a custom theme. This keeps your changes separate from the core files and makes updates easier.
-
Copy the Starter Theme
Duplicate
content/themes/starter/tocontent/themes/your-theme/ -
Modify Templates
Edit the PHP templates in
templates/to change the HTML structure. All templates use plain PHP — no special template syntax required. -
Customize Styles
Edit
assets/css/theme.cssto change colors, fonts, spacing, and layout. The Starter theme uses clean, semantic CSS that is easy to customize. -
Add JavaScript
Edit
assets/js/theme.jsfor custom interactivity. The file includes handlers for search autocomplete, newsletter subscription, and comment submission. -
Modify the Controller
Edit
controllers/FrontendController.phpto change data fetching logic, add new template variables, or modify query behavior. -
Activate Your Theme
Set
'theme' => 'your-theme'inconfig/app.php
Adding Custom Page Templates
Create a new PHP file in your theme's templates/ directory (e.g., landing.php). When editing a page in the admin panel, select your custom template from the Template dropdown.
<!-- templates/landing.php -->
<div class="landing-page">
<div class="hero-banner">
<h1><?= e($page['title']) ?></h1>
</div>
<div class="page-content">
<?= $page['content'] ?>
</div>
</div>
Modifying the Admin Panel
Admin views are located in admin/views/ and use Tailwind CSS for styling. You can modify admin templates directly, but keep in mind that changes may be overwritten during updates.
Tailwind CSS Build System
Prime Core uses the Tailwind CSS standalone CLI (v3.4.17) — no Node.js or npm required. Two separate configurations exist:
| Config | Scans | Output |
|---|---|---|
tailwind.admin.config.js | admin/views/**/*.php | admin/assets/css/tailwind.css |
tailwind.frontend.config.js | content/themes/starter/templates/**/*.php, partials/**/*.php | content/themes/starter/assets/css/tailwind.css |
After modifying any PHP templates, rebuild the CSS:
# Rebuild admin CSS ./tailwindcss -i admin/assets/css/tailwind-input.css -o admin/assets/css/tailwind.css -c tailwind.admin.config.js -m # Rebuild frontend CSS ./tailwindcss -i content/themes/starter/assets/css/tailwind-input.css -o content/themes/starter/assets/css/tailwind.css -c tailwind.frontend.config.js -m
Both configs share the same color theme: Ink (#0f172a), Rose Gold (#c2727d), Cream (#fafaf9), and Warm Gray (#f1f5f9).
core/, admin/, or index.php), document your changes carefully. These files may be overwritten during future updates. For frontend customizations, always use the theme system.
Troubleshooting
Common Issues
Blank Page / 500 Internal Server Error
- Enable debug mode: Set
'debug' => trueinconfig/app.php - Check PHP error logs (usually in
/var/log/apache2/error.logor/var/log/nginx/error.log) - Verify PHP version is 8.0 or higher:
php -v - Ensure all required PHP extensions are installed:
php -m - Check file permissions on
config/,cache/,storage/,uploads/
404 Not Found on All Pages
- Verify
mod_rewriteis enabled:a2enmod rewrite(Apache) - Ensure
.htaccessfile exists in the root directory (it may be hidden) - Check Apache configuration allows
.htaccessoverrides (AllowOverride All) - For Nginx, ensure the rewrite rules are in your server block configuration
Backup Download / Delete Returns 404
- This was a known issue where the router's default
{param}regex ([a-zA-Z0-9_-]+) did not allow dots in filenames (e.g.,backup_2026-02-14.sql) - Resolved by using constrained route parameters:
{file:[a-zA-Z0-9._-]+} - If you experience similar 404 issues with routes containing dots, use the
{param:regex}syntax in your route definitions
Admin Panel Not Loading / CSS Broken
- Ensure the
urlinconfig/app.phpmatches your actual domain (no trailing slash) - Verify that
admin/assets/css/tailwind.cssexists (rebuild with the Tailwind CLI if missing) - Verify the CSP header is not blocking required resources (check browser console)
File Uploads Failing
- Check PHP
upload_max_filesizein php.ini - Check PHP
post_max_sizein php.ini (must be larger thanupload_max_filesize) - Verify
uploads/directory is writable - Ensure the file extension is in the allowed list in
config/app.php
Database Connection Error
- Verify database credentials in
config/database.php - Ensure MySQL/MariaDB service is running
- Check that the database user has proper privileges on the database
- Try connecting manually:
mysql -u username -p -h 127.0.0.1 database_name
CSRF Token Errors
- This usually indicates session issues — check that
storage/is writable - Ensure session cookies are being set (check browser cookies)
- If you see "Invalid CSRF token" on every form, try clearing your browser cookies and logging in again
TinyMCE Editor Not Loading
- Check browser console for CSP or network errors
- Verify internet access (TinyMCE loads from CDN:
cdn.tiny.cloud) — it is lazy-loaded only on pages that need it - Ensure JavaScript is enabled in the browser
Maintenance Mode — Locked Out
- Edit
config/app.phpdirectly and set'maintenance_mode' => false - Alternatively, access the admin panel directly — logged-in admins can bypass maintenance mode
Performance Tips
- Enable caching in production (
'cache_enabled' => true) - Use PHP OPcache for bytecode caching
- Enable gzip/brotli compression on your web server
- Set appropriate
cache_ttlbased on how often content changes - Tailwind CSS is pre-built and minified — no runtime compilation overhead
- TinyMCE and Chart.js are lazy-loaded only on pages that need them, reducing initial page weight
- Responsive image variants with
srcsetlet browsers load appropriately sized images - Use a CDN for static assets if serving high traffic
Debug Mode
'debug' => true) in production. It exposes detailed error messages, stack traces, and potentially sensitive information to visitors. Use it only during development or troubleshooting, then immediately disable it.
Changelog
Version 1.0.0 Initial Release
Release Date: 2026
- Email System — Built-in SMTP mailer (
core/Mailer.php) with TLS/SSL, HTML templates, andsend_email()helper - Page Revisions — Full revision history for pages (matching the existing post revision system)
- Revision Diff & Compare — Word-level LCS diff algorithm with side-by-side comparison modal for both posts and pages
- Post Autosave — Automatic draft saving at regular intervals via AJAX
- Media Library Enhancements — Grid/list toggle, type filtering (images/docs), bulk delete, inline alt text editing with debounce, thumbnail optimization
- Responsive Images — Automatic generation of 4 image variants (original, thumb 150×150, medium 600×400, large 1200×800) with
srcsetattributes - Production Tailwind CSS — Standalone CLI (v3.4.17) producing minified builds for both admin and frontend
- Lazy-Loaded Assets — TinyMCE and Chart.js loaded on demand only on pages that need them
- Search Improvements — Enhanced search with better relevance and AJAX autocomplete
- Previous/Next Navigation — Redesigned post navigation with thumbnail previews
- Dashboard Improvements — Enhanced admin dashboard with better statistics and quick actions
- User Profile Page — Dedicated profile page accessible to all roles at
/profilewith avatar upload, password change, bio editing, and 2FA management - Notification System — Real-time admin notification bell with 60-second AJAX polling. Tracks pending comments, unread contact messages, new subscribers, and scheduled posts
- Layout Switcher — Post listing pages include a 3-mode layout switcher: Grid, List, and Magazine views. Preference persisted in localStorage
- 3-Level Navigation — Frontend header menus support three levels of nesting on both desktop and mobile
- Social Sharing — Single post pages include share buttons for Twitter/X, Facebook, LinkedIn, and Copy Link
- Reading Progress Bar — Visual progress indicator at the top of single post pages
- Table of Contents — Auto-generated from article headings on single post pages
- Author Bio Box — Author information card displayed below post content
- Related Posts — Up to 4 related posts from the same category shown below article content
- Config Recovery — 3-tier config recovery system prevents lockouts during deployment
- Demo Mode — Built-in read-only demo role for live preview environments with router-level middleware
- Hero Slider — Homepage auto-rotating featured post carousel with navigation and responsive layout
- Premium Design System — Unified Outfit + Plus Jakarta Sans + Fira Code typography with Ink/Rose-Gold color palette
- Complete CMS core — Posts, pages, categories, tags, comments, media, menus, users, newsletter, contact, SEO, backup, cache, maintenance mode
- Security — CSRF protection, prepared statements, rate limiting, IP blocking, CSP headers, TOTP two-factor authentication
- Web-based installer — Database setup and demo data seeding with install lock
Credits
Third-Party Libraries (CDN)
| Library | Version | Purpose | License |
|---|---|---|---|
| Tailwind CSS | 3.4.17 | Admin + frontend styling (production build via standalone CLI) | MIT |
| TinyMCE | 6.8.3 | Rich text editor (CDN, lazy-loaded) | MIT |
| Chart.js | 4.4.1 | Dashboard analytics charts (CDN, lazy-loaded) | MIT |
Built With
- PHP 8.0+ — Server-side language
- MySQL / MariaDB — Database engine
- PDO — Database abstraction
- Vanilla JavaScript — Frontend interactivity (no jQuery dependency)
Support
If you have questions, encounter bugs, or need assistance:
- Check this documentation first
- Review the Troubleshooting section above
- Contact us through the Prime Core website