Do
- DO change the default password and login URL immediately after setting up WonderCMS.
- DO always backup before updating.
- DO only install themes and plugins only from sources you trust.
- DO create functions.php instead of editing index.php.
Experience WonderCMS firsthand with our live demo. Explore the admin interface and test the features in a real environment.
git clone https://github.com/WonderCMS/wondercms.git
For enhanced security, it's recommended to change the default login URL of your WonderCMS website.
Changing your default login URL provides several security benefits:
// Before
Default login URL: yoursite.com/loginURL
// After
New login URL: yoursite.com/my-secret-login
For enhanced security, it's crucial to change the default password after your initial WonderCMS setup.
database.js
file directly on your server.
class Wcms {
// Core constants
public const DB_CONFIG = 'config';
public const DB_PAGES_KEY = 'pages';
// Public properties
public $currentPage;
public $loggedIn;
public $installedPlugins = [];
// Key methods
public function init(): void { ... }
public function render(): void { ... }
public function getDb(): stdClass { ... }
}
Handles JSON storage in database.js
with methods for CRUD operations
Manages admin sessions and security through:
Method | Parameters | Description |
---|---|---|
get() |
string $key | Retrieve data from database.json |
This section presents the default database.js file that is generated the first time you visit your new WonderCMS website.
{
"config": {
"siteTitle": "Website title",
"siteLang": "en",
"adminLang": "en",
"theme": "sky",
"defaultPage": "home",
"login": "loginURL",
"forceLogout": false,
"forceHttps": false,
"saveChangesPopup": false,
"modalPersistence": false,
"logoutToLoginScreen": true,
"password": "HIDDEN",
"lastLogins": {},
"lastModulesSync": "2025\/03\/15",
"customModules": {
"themes": {},
"plugins": {}
},
"menuItems": {
"0": {
"name": "Home",
"slug": "home",
"visibility": "show",
"subpages": {}
},
"1": {
"name": "How to",
"slug": "how-to",
"visibility": "show",
"subpages": {}
}
}
},
"pages": {
"404": {
"created": "2025-03-15T11:01:24+00:00",
"modified": "2025-03-15T11:01:24+00:00",
"visibility": "show",
"title": "404",
"keywords": "404",
"description": "404",
"content": "<center><h1>404 - Page not found<\/h1><\/center>",
"subpages": {}
},
"home": {
"created": "2025-03-15T11:01:24+00:00",
"modified": "2025-03-15T11:01:24+00:00",
"visibility": "show",
"title": "Home",
"keywords": "Enter, page, keywords, for, search, engines",
"description": "A page description is also good for search engines.",
"content": "<h1>Welcome to your website<\/h1>\n\n<p>",
"subpages": {}
},
"how-to": {
"created": "2025-03-15T11:01:24+00:00",
"modified": "2025-03-15T11:01:24+00:00",
"visibility": "show",
"title": "How to",
"keywords": "Enter, keywords, for, this page",
"description": "A page description is also good for search engines.",
"content": "<h2>Easy editing<\/h2>\n<p>After logging in, click anywhere to edit and click outside to save.",
"subpages": {}
}
},
"blocks": {
"subside": {
"content": "<h2>About your website<\/h2>\n\n<br>\n<p>"
},
"header": {
"content": "<nav>You can include this in your theme.php and edit it.<\/nav>"
},
"footer": {
"content": "©2025 Your website"
}
}
}
Yes, you can edit your database file directly. If you plan to do so, make sure to back it up regularly, as any small mistake can make your website temporarily unaccessible.
WonderCMS includes built-in search capabilities that theme developers can implement. The search feature provides real-time filtering and highlighting of content across pages.
To enable search in your theme, include this in your theme:
<?= $Wcms->search() ?>
Tag | Description |
---|---|
<?= $Wcms->search() ?> |
Outputs search input and required JS/CSS for search to work. |
<?php
function themeFunctions($Wcms) {
$Wcms->addListener('settings', function($content) {
return str_replace('</head>', '<style>.custom-css {}</style></head>', $content);
});
}
themeFunctions($Wcms);
WonderCMS allows you to easily customize your 404 error page to match your website's design and provide helpful information to visitors.
yoursite.com/404
WonderCMS allows you to hide pages from the main navigation while keeping them accessible through direct URLs.
To hide subpages:
These security enhancements provide granular control over admin interactions and session management. Manage them in Settings → Security.
Setting | Default | Description |
---|---|---|
forceLogout |
Disabled by default | Forces all active sessions to logout after password changes |
forceHttps |
Disabled by default | Enforces HTTPS connections site-wide. WonderCMS automatically checks for SSL, this will force to always use HTTPS. If you don't have SSL and force HTTPS, this can break your website by creating incorrect redirects. |
saveChangesPopup |
Disabled by default | Shows confirmation dialog before saving edits |
modalPersistence |
Disabled by default | Remembers last open tab in admin modals |
logoutToLoginScreen |
Enabled by default | Redirects to login screen instead of last page after logout |
When enabled: Logs out admin after password changes
When disabled: Allows admin to remain logged in
When enabled: Redirects all HTTP traffic to HTTPS
When disabled: Allows both HTTP and HTTPS connections
Requires valid SSL certificate to prevent browser warnings
When enabled: Shows "Are you sure?" dialog before saving any content
When disabled: Saves edits immediately on click-outside
When enabled: Remembers last active tab in admin panels
When disabled: Resets to first tab on each modal open
When enabled: Returns to login screen after logout
When disabled: Returns to last visited page after logout
"password": "$2y$10$AOcr4G3BUFXMWzNwV4J6ZuZsZXGjT8LZKGVXAChCaXd4kyAKvkUr6",
WonderCMS allows you to create both dynamic and static editable content areas. This section explains how to create and manage these blocks.
<?= $Wcms->page('content') ?>
<?= $Wcms->block('yourEditableBlockName') ?>
To create a new static editable block:
functions.php
in your theme folder<?php
function newEditableArea() {
global $Wcms;
// Create block if it doesn't exist
if (empty($Wcms->get('blocks','newEditableArea'))) {
$Wcms->set('blocks','newEditableArea', 'content', 'Your content here.');
}
// Get block content
$value = $Wcms->get('blocks','newEditableArea','content');
// Set default content if empty
if (empty($value)) {
$value = 'Empty content';
}
// Return editable or static content
return $Wcms->loggedIn
? $Wcms->block('newEditableArea')
: $value;
}
?>
<?= newEditableArea() ?>
<?= $Wcms->block('newEditableArea') ?>
For easier management of multiple editable areas:
plugins
folderCustom modules allow you to create, share, and install themes and plugins in WonderCMS. This section explains how to create and manage custom modules.
To create a custom module, you need to include specific files and structure your module correctly.
theme-folder/
├── theme.php # Main template file
├── css/
│ └── style.css # Theme styles
├── preview.jpg
└── wcms-modules.json # Theme metadata
{
"version": 1,
"themes": {
"theme-name": {
"name": "Theme Name",
"repo": "https://github.com/yourUsername/theme-name/tree/master",
"zip": "https://github.com/yourUsername/theme-name/archive/master.zip",
"summary": "Theme description",
"version": "1.0.0",
"image": "https://raw.githubusercontent.com/yourUsername/theme-name/master/preview.jpg"
}
}
}
plugin-name/
├── plugin-name.php
├── preview.jpg
└── wcms-modules.json
{
"version": 1,
"plugins": {
"plugin-name": {
"name": "Plugin Name",
"repo": "https://github.com/yourUsername/plugin-name/tree/master",
"zip": "https://github.com/yourUsername/plugin-name/archive/master.zip",
"summary": "Plugin description",
"version": "1.0.0",
"image": "https://raw.githubusercontent.com/yourUsername/plugin-name/master/preview.jpg"
}
}
}
version: 1
at the top of wcms-modules.json is required and should not be modified unless you know what you are doing. :)
To update a module:
To share your module with others:
WonderCMS provides a powerful hooks system that allows you to modify CMS behavior without modifying core files. Hooks are event listeners that let you execute custom code at specific points in the application lifecycle.
// Register a hook
$Wcms->addListener('hook_name', function($args) {
// Modify arguments or perform actions
return $args;
});
// Add custom CSS to all pages
$Wcms->addListener('css', function($args) {
$customCSS = '<link rel="stylesheet" href="custom.css">';
$args[0] .= $customCSS;
return $args;
});
// Add custom JS to all pages
$Wcms->addListener('js', function($args) {
$customJS = '<script src="custom.js"></script>';
$args[0] .= $customJS;
return $args;
});
// Modify page content before rendering
$Wcms->addListener('page', function($args) {
// $args[0] contains the content
// $args[1] contains the context (e.g., 'content', 'title')
if ($args[1] === 'content') {
$args[0] = str_replace('foo', 'bar', $args[0]);
}
return $args;
});
// Add custom content to settings page
$Wcms->addListener('settings', function($content) {
return str_replace('</head>',
'<style>.custom-settings { color: red; }</style></head>',
$content);
});
// Modify content before saving
$Wcms->addListener('before_save', function($content) {
// Sanitize content before saving
$content[0] = strip_tags($content[0], '<p><a><strong><em>');
return $content;
});
// Modify HTML after page rendering
$Wcms->addListener('after_render', function($html) {
// Add analytics script before closing body tag
return str_replace('</body>',
'<script>/* Analytics code */</script></body>',
$html);
});
// Process uploaded files
$Wcms->addListener('file_upload', function($fileData) {
// Validate file type and size
if ($fileData['size'] > 5000000) { // 5MB limit
throw new Exception('File size too large');
}
return $fileData;
});
The search hook allows you to modify or extend the search functionality in WonderCMS. This hook is triggered when the search action is performed.
<?php
$Wcms->addListener('search', function($args) {
// $args[0] contains the search results HTML
$results = $args[0];
// Add custom wrapper around search results
$args[0] = '<div class="custom-search-results">' . $results . '</div>';
return $args;
});
?>
<?php
$Wcms->addListener('search', function($args) {
$searchTerm = $_GET['search'] ?? '';
if (!empty($searchTerm)) {
$highlighted = '<span class="search-highlight">' . $searchTerm . '</span>';
$args[0] = str_ireplace($searchTerm, $highlighted, $args[0]);
}
return $args;
});
?>
Hook | Description | Arguments |
---|---|---|
css |
Add or modify CSS files | string $styles |
js |
Add or modify JavaScript | string $scripts |
page |
Modify page content | string $content, string $context |
settings |
Modify admin settings page | string $content |
menu |
Modify menu HTML output | string $menuHtml |
footer |
Modify footer content | string $footerContent |
before_save |
Before saving content | array $content |
after_render |
After page rendering | string $html |
file_upload |
During file upload | array $fileData |
login_success |
After successful login | array $data (ip, timestamp, user_agent) |
login_failed |
After failed login attempt | array $data (ip, timestamp, user_agent) |
search |
Modify search results | string $results |
1. css
2. js
3. before_save
4. after_render
5. file_upload
6. search
theme/
├── theme.php # Main template file
├── css/
│ └── style.css # Theme styles
├── wcms-modules.json # Theme metadata
└── functions.php # Optional theme functions
Creating custom themes in WonderCMS is straightforward and flexible. Follow these steps to create your own theme:
<?php global $Wcms ?>
<!DOCTYPE html>
<html lang="<?= $Wcms->get('config', 'siteLang') ?>">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= $Wcms->get('config', 'siteTitle') ?> - <?= $Wcms->page('title') ?></title>
<?= $Wcms->css() ?>
<link rel="stylesheet" href="<?= $Wcms->asset('css/style.css') ?>">
</head>
<body>
<?= $Wcms->settings() ?>
<?= $Wcms->alerts() ?>
<nav>
<a href="<?= $Wcms->url() ?>">
<?= $Wcms->siteTitle() ?>
</a>
<?= $Wcms->menu() ?>
</nav>
<main>
<?= $Wcms->page('content') ?>
</main>
<aside>
<?= $Wcms->block('subside') ?>
</aside>
<footer>
<?= $Wcms->footer() ?>
</footer>
<?= $Wcms->js() ?>
</body>
</html>
{
"version": 1,
"themes": {
"name": "Theme Name",
"repo": "https://github.com/yourUsername/theme-name/tree/master",
"zip": "https://github.com/yourUsername/theme-name/archive/master.zip",
"summary": "Theme description",
"version": "1.0.0",
"image": "https://raw.githubusercontent.com/yourUsername/theme-name/master/preview.jpg"
}
}
Tag | Description |
---|---|
<?= $Wcms->css() ?> |
Includes admin CSS and allows plugins to add CSS |
<?= $Wcms->js() ?> |
Includes admin JavaScript and allows plugins to add JS |
<?= $Wcms->get('config', 'siteTitle') ?> |
Returns website title, as set in admin settings |
<?= $Wcms->get('getSiteLanguage') ?> |
Returns website language |
<?= $Wcms->settings() ?> |
Displays admin settings panel |
<?= $Wcms->alerts() ?> |
Displays system alerts and messages |
<?= $Wcms->header() ?> |
Can be used to include your header, by default includes an empty navigation |
<?= $Wcms->search() ?> |
Creates search bar, which can get content from all pages and blog posts |
<?= $Wcms->menu() ?> |
Outputs the navigation menu |
<?= $Wcms->page('content') ?> |
Displays the main page content |
<?= $Wcms->page('description') ?> |
Displays the page description |
<?= $Wcms->page('keywords') ?> |
Displays the page keywords |
<?= $Wcms->page('title') ?> |
Displays page name |
<?= this('url') ?> |
Returns URL of where WonderCMS is installed |
<?= $Wcms->block('subside') ?> |
Displays a static content block |
<?= $Wcms->footer() ?> |
Displays the footer content |
<?= $Wcms->asset('path') ?> |
Returns URL to theme assets |
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?page=\$1 [L,QSA]
</IfModule>
server {
listen 80;
server_name example.com;
root /var/www/html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
}
}
example.com {
root * /var/www/html
php_fastcgi unix//var/run/php/php-fpm.sock
file_server
rewrite {
to {path} {path}/ /index.php?page={path}
}
}
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="WonderCMS Routing">
<match url="^(.*)$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="index.php?page={R:1}" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
For local development, you can use PHP's built-in web server to quickly run your WonderCMS installation.
router.php
in your WonderCMS root directoryrouter.php
:<?php
if(pathinfo($_SERVER["REQUEST_URI"], PATHINFO_EXTENSION)) {
// Serve asset files directly
return false;
} else {
// Handle WonderCMS routing
$_GET['page'] = parse_url($_SERVER["REQUEST_URI"])['path'];
include 'index.php';
}
?>
php -S localhost:8090 router.php
http://localhost:8090
For proper logout functionality:
index.php
logout&token=
logout?token=
WonderCMS provides easy access to your website's data through the $Wcms->get()
function. This section explains how to retrieve various types of data.
To retrieve data for a specific page:
<?php echo $Wcms->get('pages', 'PAGENAME')->PARAM; ?>
Parameter | Description | Example |
---|---|---|
content |
Page content | <?php echo $Wcms->get('pages', 'about')->content; ?> |
title |
Page title | <?php echo $Wcms->get('pages', 'about')->title; ?> |
description |
Page description | <?php echo $Wcms->get('pages', 'about')->description; ?> |
keywords |
Page keywords | <?php echo $Wcms->get('pages', 'about')->keywords; ?> |
created |
Date of page creation | <?php echo $Wcms->get('pages', 'about')->created; ?> |
modified |
Date of page last modified | <?php echo $Wcms->get('pages', 'about')->modified; ?> |
visibility |
Status of page visibility (controlled and synced with menu items) | <?php echo $Wcms->get('pages', 'about')->visibility; ?> |
To retrieve data for the current page:
<?php echo $Wcms->page('PARAM'); ?>
<?php echo $Wcms->page('content'); ?> // Current page content
<?php echo $Wcms->page('title'); ?> // Current page title
<?php echo $Wcms->page('description'); ?> // Current page description
<?php echo $Wcms->page('keywords'); ?> // Current page keywords
To retrieve site-wide configuration settings:
<?php echo $Wcms->get('config', 'PARAM'); ?>
Parameter | Description | Example |
---|---|---|
siteTitle |
Site title | <?php echo $Wcms->get('config', 'siteTitle'); ?> |
siteDescription |
Site description | <?php echo $Wcms->get('config', 'siteDescription'); ?> |
siteKeywords |
Site keywords | <?php echo $Wcms->get('config', 'siteKeywords'); ?> |
siteLang |
Site language | <?php echo $Wcms->get('config', 'siteLang'); ?> |
theme |
Active theme | <?php echo $Wcms->get('config', 'theme'); ?> |
login |
Login URL | <?php echo $Wcms->get('config', 'login'); ?> |
forceHttps |
HTTPS enforcement | <?php echo $Wcms->get('config', 'forceHttps'); ?> |
To retrieve data from static blocks:
<?php echo $Wcms->get('blocks', 'BLOCKNAME', 'content'); ?>
<?php echo $Wcms->get('blocks', 'footer', 'content'); ?>
To retrieve menu items:
<?php echo $Wcms->get('menu', 'items'); ?>
<?php
$menuItems = $Wcms->get('menu', 'items');
foreach ($menuItems as $item) {
echo $item->name . ' - ' . $item->slug . '<br>';
}
?>
To retrieve plugin-specific data:
<?php echo $Wcms->get('plugins', 'PLUGINNAME', 'PARAM'); ?>
<?php echo $Wcms->get('plugins', 'blog', 'posts'); ?>
To retrieve the entire database:
<?php echo print_r($Wcms->get('database'), true); ?>
print_r()
or var_dump()
to inspect the structure of the data returned by the $Wcms->get()
function.
This guide has covered the essential aspects of working with WonderCMS, including:
WonderCMS provides the $Wcms->set()
function to modify and save data in the database. This section explains how to use this function effectively.
<?php
$Wcms->set('CATEGORY', 'ITEM', 'PARAM', 'VALUE');
<?php
$Wcms->set('config', 'siteTitle', 'My website title');
<?php
$Wcms->set('config', 'theme', 'default');
<?php
$Wcms->set('pages', 'Page-name', 'title', 'Your title here');
<?php
$Wcms->set('pages', 'Page-name', 'content', 'Page content here');
Parameter | Description | Example Values |
---|---|---|
CATEGORY |
Main data category | config , pages , blocks |
ITEM |
Specific item to modify | siteTitle , Page-name , blockName |
PARAM |
Specific parameter to set | title , content , keywords |
VALUE |
Value to set | String or array of data |
$Wcms->set()
in:
functions.php
For more complex data structures, you can pass arrays as values:
<?php
$Wcms->set('config', 'customData', [
'key1' => 'value1',
'key2' => 'value2',
'nested' => [
'subKey' => 'subValue'
]
]);
This section covers common issues encountered when using WonderCMS and their solutions.
Symptoms: "Sorry, page not found" error when visiting login URL
Options -Indexes
ServerSignature Off
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)$ index.php?page=$1 [QSA,L]
RewriteRule database.js - [F]
RewriteRule cache.json - [F]
Solution: Set file permissions to 644 and folder permissions to 755
If you're seeing a persistent update message even when your WonderCMS is up-to-date, this section explains the possible causes and solutions.
WonderCMS performs two checks to determine if an update is available:
index.php
If these versions differ or the check fails, the update message appears.
Symptoms: Running WonderCMS locally without proper SSL certificates
Solution: Install a certificate on your local computer
Symptoms: Hosting environment lacks required components
Solution: Verify your hosting meets WonderCMS requirements
Consider using our recommended hosting partner
Symptoms: Hosting environment lacks cURL extension
Solution:
sudo apt-get install php-curl
sudo yum install php-curl
Configure web.config for clean URLs
public static function url($location = null) {
return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 'https://' : 'http://')
. $_SERVER['HTTP_HOST']
. str_replace($_SERVER['DOCUMENT_ROOT'], '',
str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']))
. "/{$location}";
}
Solution:
/etc/apache2/apache2.conf
AllowOverride None
to AllowOverride All
<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
Solution: Set correct ownership of the directory where WonderCMS is installed to www-data
If you encounter an error not listed here, please report it in our GitHub issues section.