Developer Guide
Learn how to create modules, themes, and language packs for SovereignCMS.
Creating Modules
Modules extend SovereignCMS with new features. Each module has its own routes, views, assets, and database tables.
Module Structure
module.json # Module metadata and config
index.js # Entry point with hooks
views/
admin/ # Admin panel templates
public/ # Public-facing templates
assets/
css/
js/
languages/
en/
modules/
my-module.json
module.json Configuration
{
"name": "My Module",
"slug": "my-module",
"version": "1.0.0",
"description": "Module description here",
"author": "Your Name",
"icon": "fas fa-puzzle-piece",
"category": "content",
"tags": ["feature", "utility"],
"requires": {
"cms": ">=1.0.0"
},
"hooks": {
"install": true,
"uninstall": true,
"activate": true,
"deactivate": true,
"init": true
},
"routes": {
"public": "/m/my-module",
"admin": "/admin/modules/my-module"
},
"permissions": [
"mymodule.view",
"mymodule.create",
"mymodule.edit",
"mymodule.delete"
],
"config": {
"itemsPerPage": 10,
"enableFeature": true
},
"adminMenu": {
"label": "My Module",
"icon": "fas fa-puzzle-piece",
"position": 50,
"items": [
{
"label": "Dashboard",
"route": "/admin/modules/my-module",
"permission": "mymodule.view"
}
]
}
}
Module Entry Point (index.js)
const express = require('express');
module.exports = {
// Called when module is first installed
async install({ db }) {
await db.query(`
CREATE TABLE IF NOT EXISTS mm_items (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
slug VARCHAR(255) UNIQUE NOT NULL,
description TEXT,
is_active TINYINT(1) DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
},
// Called when module is removed
async uninstall({ db }) {
await db.query('DROP TABLE IF EXISTS mm_items');
},
// Called when module is enabled
async activate({ db }) {
console.log('Module activated');
},
// Called when module is disabled
async deactivate({ db }) {
console.log('Module deactivated');
},
// Called on every server start
async init({ db, app }) {
console.log('Module initialized');
},
// Public routes (mounted at /m/my-module/)
async routes(context) {
const { db, validate, module } = context;
const router = express.Router();
router.get('/', async (req, res) => {
const items = await db.query('SELECT * FROM mm_items WHERE is_active = 1');
res.render('modules/my-module/views/public/index', { items });
});
return router;
},
// Admin routes (mounted at /admin/modules/my-module/)
async adminRoutes(context) {
const { db, validate, module } = context;
const router = express.Router();
router.get('/', async (req, res) => {
const items = await db.query('SELECT * FROM mm_items');
res.render('modules/my-module/views/admin/index', { items });
});
return router;
}
};
Tip: Prefix your database tables with a unique identifier (e.g., mm_ for "my-module") to avoid conflicts with other modules.
Creating Themes
Themes control the visual appearance of SovereignCMS. They use EJS templates and CSS with customizable variables.
Theme Structure
theme.json # Theme metadata and customization schema
thumbnail.png # Preview image
layouts/
main.ejs # Main layout wrapper
pages/
default-home.ejs
page.ejs
search.ejs
account/ # User account pages
auth/ # Login, register, etc.
errors/ # 404, 500, etc.
partials/ # Reusable components
assets/
css/
theme.css
js/
images/
theme.json Configuration
{
"name": "My Theme",
"slug": "my-theme",
"version": "1.0.0",
"description": "A beautiful theme for SovereignCMS",
"author": "Your Name",
"author_url": "https://yoursite.com",
"license": "MIT",
"preview": "thumbnail.png",
"customization": {
"colors": {
"label": "Colors",
"fields": {
"primary_color": {
"type": "color",
"label": "Primary Color",
"default": "#8b5cf6"
},
"secondary_color": {
"type": "color",
"label": "Secondary Color",
"default": "#06b6d4"
},
"background_color": {
"type": "color",
"label": "Background",
"default": "#0a0118"
},
"text_color": {
"type": "color",
"label": "Text Color",
"default": "#ffffff"
}
}
},
"typography": {
"label": "Typography",
"fields": {
"font_family": {
"type": "select",
"label": "Font Family",
"options": ["Inter", "Roboto", "Open Sans", "Poppins"],
"default": "Inter"
},
"base_font_size": {
"type": "number",
"label": "Base Font Size (px)",
"min": 12,
"max": 20,
"default": 16
}
}
},
"header": {
"label": "Header",
"fields": {
"header_style": {
"type": "select",
"label": "Header Style",
"options": ["default", "centered", "minimal"],
"default": "default"
},
"sticky_header": {
"type": "boolean",
"label": "Sticky Header",
"default": true
}
}
}
},
"zones": {
"header": {
"label": "Header",
"widgets": ["logo", "menu", "search", "user-menu"]
},
"main": {
"label": "Main Content",
"widgets": ["*"]
},
"sidebar": {
"label": "Sidebar",
"widgets": ["*"]
},
"footer": {
"label": "Footer",
"widgets": ["text-block", "menu", "social-links"]
}
}
}
Customization Field Types
- color - Color picker
- text - Text input
- number - Numeric input with min/max
- select - Dropdown with options array
- boolean - Toggle/checkbox
- image - Image upload
Use depends_on to conditionally show fields (e.g., "depends_on": "header.sticky_header")
Using CSS Variables
Customization values are injected as CSS variables. Use them in your theme CSS:
/* Variables are automatically injected from theme customization */
:root {
--primary: var(--theme-primary-color, #8b5cf6);
--secondary: var(--theme-secondary-color, #06b6d4);
--bg: var(--theme-background-color, #0a0118);
}
.header {
background: var(--primary);
}
.button {
background: linear-gradient(135deg, var(--primary), var(--secondary));
}
Creating Language Packs
SovereignCMS supports 37+ languages with a comprehensive translation system. Language packs consist of JSON files organized by category.
Language Pack Structure
config.json # Language metadata
en/ # English (or your language code)
core.json # Core system translations
admin.json # Admin panel
account.json # User account pages
pages.json # Page templates
validation.json # Validation messages
modules/ # Module translations
blog-manager.json
config.json Language Entry
{
"es": {
"name": "Spanish",
"native_name": "Español",
"flag": "es",
"direction": "ltr",
"locale": "es_ES",
"date_format": "DD/MM/YYYY",
"time_format": "HH:mm",
"number_format": {
"decimal": ",",
"thousands": ".",
"currency_symbol": "€",
"currency_position": "after"
},
"pluralization": "spanish",
"active": true
}
}
Translation File Format
{
"common": {
"save": "Guardar",
"cancel": "Cancelar",
"delete": "Eliminar",
"edit": "Editar",
"view": "Ver",
"search": "Buscar",
"loading": "Cargando..."
},
"navigation": {
"home": "Inicio",
"profile": "Perfil",
"settings": "Configuración",
"logout": "Cerrar sesión"
},
"errors": {
"404": "Página no encontrada",
"500": "Error del servidor",
"generic": "Algo salió mal"
}
}
Module Translation File
{
"module": {
"name": "Gestor de Blogs",
"description": "Sistema de gestión de múltiples blogs"
},
"common": {
"blog": "Blog",
"blogs": "Blogs",
"post": "Publicación",
"posts": "Publicaciones"
},
"public": {
"allBlogs": "Todos los Blogs",
"browseBlogs": "Explora nuestra colección de blogs",
"noBlogs": "No hay blogs disponibles."
},
"admin": {
"manageBlogs": "Gestionar Blogs",
"createNewBlog": "Crear Nuevo Blog"
}
}
Using Translations
<%= t('core.common.save') %>
<%= t('modules.blog-manager.public.allBlogs') %>
<%= t('core.errors.404') %>
Important: Use the English language pack as a reference to ensure all translation keys are included. Missing keys will fall back to English automatically.
RTL Support: For right-to-left languages (Arabic, Hebrew, Urdu, Persian), set "direction": "rtl" in the language config.
Animated Backgrounds
Animated backgrounds use CSS gradients and pseudo-elements for performance. Theme customization controls the colors.
Background CSS Pattern
/* Base gradient background */
body {
background: linear-gradient(135deg, var(--bg) 0%, var(--bg-dark) 100%);
}
/* Animated nebula effect using pseudo-element */
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background:
/* Purple glow */
radial-gradient(circle at 20% 80%, rgba(139, 92, 246, 0.15) 0%, transparent 50%),
/* Cyan glow */
radial-gradient(circle at 80% 20%, rgba(6, 182, 212, 0.1) 0%, transparent 50%),
/* Amber accent */
radial-gradient(circle at 40% 40%, rgba(245, 158, 11, 0.05) 0%, transparent 50%);
pointer-events: none;
z-index: -1;
animation: nebulaPulse 10s ease-in-out infinite alternate;
}
@keyframes nebulaPulse {
from { opacity: 0.8; }
to { opacity: 1; }
}
Submission Checklist
Before submitting your product to the marketplace:
- All configuration files (module.json / theme.json) are valid JSON
- Preview image/screenshot is included (PNG, 1200x800 recommended)
- Version number follows semantic versioning (MAJOR.MINOR.PATCH)
- CMS compatibility version is specified in "requires"
- No hardcoded paths - use relative paths only
- Tested on a fresh SovereignCMS installation
- ZIP contains only the product folder at root level
- Includes English translations as base language