Comprehensive Documentation - Version 2.0
composer install to install dependencies..env file to configure environment and database settings.
database.default.hostname = localhost
database.default.database = igniter_cms_db
database.default.username = root
database.default.password = db_password
database.default.DBDriver = MySQLi
database.default.DBPrefix =
database.default.port = 3306
.env file. app.baseURL = 'http://localhost/igniter-cms/'php spark generate:key. This command will generate/update the application key (APP_KEY) in .env file.php spark recreate:tables.https://localhost/igniter-cms/.
Note: To reset database you can use these commands php spark recreate:tables
htdocs, www, or public_html).
http://localhost/igniter-cms-installer/).
.env file, set up the database, install dependencies
/install folder should be deleted after installation. Note it would be automatically removed in "production" environment for security.
Ensure writable and public/uploads directories are writable by the web server.
The dashboard is the landing page of the backend and includes the following features:
The CMS module allows you to manage the website's content, including blogs, pages, and navigations.
Create, update, and remove blogs. Features include:
Manage blog categories. Features include:
Create, update, and remove pages. Features include:
Organize and manage structured data entries for various purposes. Features include:
The Form Manager provides a robust toolset for creating, managing, and tracking various types of user-submitted forms directly from the admin panel.
Use to manage contact form submissions
Use to manage booking form submissions
Use to manage subscription form submissions
Upload, manage, and organize files, including images and documents.
Manage application themes here. Add, Active or remove themes.
Edit theme files here. Uses code editor to edit file contents.
The CMS includes a built-in versioning system for your theme files, allowing you to experiment with code changes safely and revert to previous versions if something goes wrong.
Revisions are created automatically whenever you save a file in the Theme Editor:
/account/appearance/theme-editor)._layout.php or site.css).To view your history or restore an old version of your theme, navigate to Appearance > Revisions (/account/appearance/theme-editor/revisions).
This is the module for managing your account details and changing passwords
Update your account information (name, bio, etc.).
Change your account password.
The Admin module provides features for managing users, system configurations, and website functionality.
Create, edit, and manage user accounts and their permissions.
| Module | Admin | Manager |
|---|---|---|
| Dashboard | ||
CMS
|
||
| File Manager | ||
Settings
|
||
Admin
|
||
| Manage Themes | ||
| Plugins | ||
| Documentation | ||
| AI Assistance |
Adjust application settings and configurations to suit your needs.
Edit header and footer JavaScript as well as custom CSS.
Generate and manage API keys for accessing CMS data via the API.
View a history of user actions and system events for tracking changes.
Track system events and actions with detailed logs. Features include:
Analyze website traffic statistics and view detailed analytics.
Manage and restrict access from unwanted IP addresses. Features include:
Allow specific IP addresses for privileged access. Features include:
Create and manage backups for the database and website files.
Edit theme files directly within the admin panel (use this feature with caution).
Manage installed plugins.
Manage plugin's configuration data.
Enhance productivity and streamline workflows with AI-powered features tailored for Igniter CMS.
Get instant AI-powered assistance for any questions related to Igniter CMS. Features include:
Generate content dynamically to improve site efficiency and SEO ranking. Features include:
Leverage AI to gain insights into site performance and operational data. Features include:
This section outlines various methods to deploy your Igniter CMS application to a live server. Regardless of the deployment method chosen, you will always need to create and restore your database on the server.
cPanel).Deploying via FTP involves manually transferring files and setting up your database.
cPanel).phpMyAdmin (or your preferred database management tool).phpMyAdmin in your hosting control panel and import the exported database file into the newly created database.FileZilla) with your FTP credentials.public_html or a subdirectory)..env file in your uploaded project root on the server and update the database connection details (database name, username, password).app/Config/Database.php on the server and ensure the database connection settings are correct.app/Config/App.php on the server and set the baseURL to your domain (e.g., https://your-domain.com/).This method leverages a Continuous Integration/Continuous Deployment pipeline using GitHub Actions for automated deployment.
.github/workflows/ directory in your local Igniter CMS project.main.yaml.txt to main.yaml.main.yaml file.# symbol from the beginning of relevant lines.server key with your FTP server address (e.g., server: ftp.your-domain.com).server-dir key with the path to your website's directory on the server (e.g., server-dir: /your-site-path).FTPUSERNAME and FTPPASSWORD, using your FTP account details..env file with the production database connection details.app/Config/Database.php with the production database connection settings.baseURL in your local app/Config/App.php to your domain.vendor folder (e.g., vendor.zip).vendor.zip to your server's deployment directory (e.g., using FTP or File Manager).vendor.zip file in the server's deployment directory. The vendor folder is typically not pushed to Git due to its size and dynamic nature..env File:
.env file directly on your server in the root of your deployed application directory. This file will not be pushed by Git. Configure it with your production environment variables, especially database credentials.main.yaml and updated configurations).cPanel).phpMyAdmin (or your preferred database management tool).phpMyAdmin in your hosting control panel and import the exported database file into the newly created database.This method utilizes your hosting provider's file manager to upload your entire project as a compressed archive.
.env file with the production database connection details.app/Config/Database.php with the production database connection settings.baseURL in your local app/Config/App.php to your domain.cPanel).phpMyAdmin (or your preferred database management tool).phpMyAdmin in your hosting control panel and import the exported database file into the newly created database.cPanel).public_html or a specific subdirectory).0755 for directories and 0644 for filesYou can set the CMS as a headless CMS or use the Views to render data. To set the format for frontend, got the Admin > Configurations and search FrontEndFormat. Set it to either MVC or API
The app includes fetch-only APIs for retrieving CMS data.
/api/{api-key}/get-model-data?model=navigations&take=10&skip=0: Generic - Fetch navigations (10 items, skip 0)./api/{api-key}/get-model-data?model=categories&where_clause={"status":1}: Generic - Fetch filtered categories (e.g., status = 1)./api/{api-key}/get-model-data?model=blogs&where_clause={"blog_id":"{blog-id}"}: Generic - Fetch filtered blogs (e.g., blog_id = {blog-id})./api/{api-key}/get-model-data?model=blogs&where_clause={"blog_id":"{blog-id}","status":1}: Generic - Fetch multiple filtered blogs (e.g., blog_id = {blog-id} and status = 1).| Model Name | Corresponding Model |
|---|---|
| blockedIPsModel | App\Models\BlockedIPsModel |
| blogs | App\Models\BlogsModel |
| categories | App\Models\CategoriesModel |
| codes | App\Models\CodesModel |
| contentBlocks | App\Models\ContentBlocksModel |
| dataGroupsModel | App\Models\DataGroupsModel |
| navigations | App\Models\NavigationsModel |
| pages | App\Models\PagesModel |
| themes | App\Models\ThemesModel |
| whitelistedIPsModel | App\Models\WhitelistedIPsModel |
| Model Name | Model Fields |
|---|---|
| BlogsModel |
blog_id(VARCHAR), title(VARCHAR), slug(VARCHAR), featured_image(VARCHAR), excerpt(VARCHAR),
content(TEXT), category(VARCHAR), tags(VARCHAR), is_featured(BOOLEAN), status(INT), meta_title(VARCHAR),
meta_description(TEXT), meta_keywords(TEXT), created_by(DATETIME), updated_by(DATETIME)
|
| BlockedIPsModel |
blocked_ip_id(VARCHAR), ip_address(VARCHAR), country(VARCHAR), block_start_time(DATETIME),
block_end_time(DATETIME), reason(VARCHAR), notes(VARCHAR), page_visited_url(VARCHAR)
|
| CategoriesModel |
category_id(VARCHAR), title(VARCHAR), description(VARCHAR), group(VARCHAR), parent(VARCHAR),
link(VARCHAR), new_tab(BOOLEAN), order(INT), status(INT), created_by(DATETIME), updated_by(DATETIME)
|
| CodesModel |
code_id(VARCHAR), code_for(VARCHAR), code(VARCHAR), deletable(VARCHAR),
created_by(DATETIME), updated_by(DATETIME)
|
| ContentBlocksModel |
content_id(VARCHAR), identifier(VARCHAR), author(VARCHAR), title(VARCHAR), description(VARCHAR),
content(TEXT), icon(VARCHAR), group(VARCHAR), image(VARCHAR), link(VARCHAR), new_tab(BOOLEAN),
order(INT), custom_field(VARCHAR), created_by(DATETIME), updated_by(DATETIME)
|
| DataGroupsModel |
data_group_id(VARCHAR), data_group_for(VARCHAR), data_group_list(VARCHAR),
deletable(VARCHAR), created_by(VARCHAR), updated_by(VARCHAR)
|
| ThemesModel |
theme_id(VARCHAR), name(VARCHAR), path(VARCHAR), primary_color(VARCHAR), secondary_color(VARCHAR), other_color(VARCHAR),
image(VARCHAR), theme_url(VARCHAR), footer_copyright(VARCHAR), category(VARCHAR), sub_category(VARCHAR), selected(BOOLEAN),
deletable(BOOLEAN), home_page(VARCHAR), created_by(DATETIME), updated_by(DATETIME)
|
| WhitelistedIPsModel |
whitelisted_ip_id(VARCHAR), ip_address(VARCHAR), reason(VARCHAR)
|
Customize the messages in the app in app/Config/CustomConfig.php.
class CustomConfig extends BaseConfig
{
#--------------------------------------------------------------------
# MESSAGES
#--------------------------------------------------------------------
public $wrongCredentialsMsg = 'Sign In Failed. The provided username/email or password is incorrect.';
public $loginSuccessMsg = 'Login successful.';
public $logoutSuccessMsg = 'You have been successfully logged out.';
public $pendingActivationMsg = 'Your account has not been activated yet or is no longer active. Please contact the administrator.';
public $tooManyFailedLogins = 'Too many failed login attempts. Your IP has been blocked for 1 hour.';
public $invalidAccessMsg = 'You do not have access to this area.';
public $createSuccessMsg = 'Record created successfully.';
public $editSuccessMsg = 'Record updated successfully.';
public $deleteSuccessMsg = 'Record removed successfully.';
public $missingRequiredInputsMsg = 'There are validation errors. Possible missing required inputs.';
public $sentContactMsg = 'Message sent successfully.';
public $failedContactMsg = 'Form submission failed.';
public $notFoundMsg = 'Record not found.';
public $alreadyExistMsg = '[Record] already exists in table.';
public $errorMsg = 'Oops! Something went wrong. Please try again later.';
public $resetLinkMsg = 'A password reset link has been sent to your email address. Please check your inbox and follow the instructions to reset your password. If you do not see the email in your inbox, please check your spam or junk folder.';
public $invalidResetLinkMsg = 'Invalid or expired password reset link.';
public $passwordResetRequiredMsg = 'For security reasons, you need to change your password before continuing. Your current password was either set by an administrator or is a default password.';
public $passwordResetSuccessfulMsg = 'Your password has been reset successfully. You can now log in with your new password.';
public $passwordResetFailedMsg = 'Unable to reset password. Please try again';
public $nonExistingResetEmailMsg = 'We are sorry, but the email address you entered is not associated with any account. Please check the email address and try again.';
public $exceptionMsg = 'There was an error processing your request. Please try again. If this error persists, please see or send an email to system administrator.';
public $contactMessageSuccessful = 'Your message has been sent successfully.';
public $contactMessageFailed = 'Oops! Something went wrong with your message submission. Please try again later.';
public $bookingSuccessful = 'Your booking has been made successfully.';
public $bookingFailed = 'Oops! Something went wrong with your booking submission. Please try again later.';
public $subscriptionSuccessful = 'You have successfully subscribed!';
public $subscriptionFailed = 'Sorry, something went wrong with your subscription. Please try again.';
#--------------------------------------------------------------------
# THEME CATEGORIES
#--------------------------------------------------------------------
public $themeCategories = [
'Business' => 'Business & Corporate',
'Ecommerce' => 'Ecommerce',
'Portfolio' => 'Portfolio & Resume',
'News' => 'Blog & News',
'Events' => 'Event & Booking Websites',
'Educational' => 'Educational & Membership Websites',
'Restaurant' => 'Restaurant & Hospitality Websites',
'Health' => 'Health & Wellness Websites',
'Directory' => 'Directory & Listing Websites',
'Entertainment' => 'Entertainment Websites',
'General' => 'General',
];
#--------------------------------------------------------------------
# USER ROLES
#--------------------------------------------------------------------
public $userRoles = [
'Admin' => 'Admin',
'Manager' => 'Manager',
'User' => 'User',
];
}
Customize the data for activity types in logs in app/Constants/ActivityTypes.php.
class ActivityTypes
{
//AUTH LOGS
const USER_REGISTRATION = 'user_registration';
const FAILED_USER_REGISTRATION = 'failed_user_registration';
const USER_LOGIN = 'user_login';
const USER_LOGOUT = 'user_logout';
const FAILED_USER_LOGIN = 'failed_user_login';
//CONTACT LOGS
const CONTACT_CREATION = 'contact_created';
const FAILED_CONTACT_CREATION = 'failed_contact_creation';
const CONTACT_UPDATE = 'contact_updated';
const FAILED_CONTACT_UPDATE = 'failed_contact_update';
const CONTACT_DELETION = 'contact_delete';
//USER LOGS
const USER_CREATION = 'user_created';
const FAILED_USER_CREATION = 'failed_user_creation';
const USER_UPDATE = 'user_updated';
const FAILED_USER_UPDATE = 'failed_user_update';
const USER_DELETION = 'user_delete';
.........................................................
/**
* Gets the description for a given activity type.
*
* @param string $type The activity type.
* @return string The description of the activity type, or "Unknown Activity" if not found.
*/
public static function getDescription($type)
{
$descriptions = [
//Auth
self::USER_REGISTRATION => 'User Registration',
self::FAILED_USER_REGISTRATION => 'User Registration Failed',
self::USER_LOGIN => 'User Login',
self::USER_LOGOUT => 'User Logout',
self::FAILED_USER_LOGIN => 'Failed User Login',
//Contact
self::CONTACT_CREATION => 'Contact Creation',
self::FAILED_CONTACT_CREATION => 'Contact Creation Failed',
self::CONTACT_UPDATE => 'Contact Update',
self::FAILED_CONTACT_UPDATE => 'Contact Update Failed',
self::CONTACT_DELETION => 'Contact Deletion',
//User
self::USER_CREATION => 'User Creation',
self::FAILED_USER_CREATION => 'User Creation Failed',
self::USER_UPDATE => 'User Update',
self::FAILED_USER_UPDATE => 'User Update Failed',
self::USER_DELETION => 'User Deletion',
.........................................................
];
return $descriptions[$type] ?? 'Unknown Activity';
}
}
Customize the files by going to app/Views/front-end/themes/{theme-folder}.
Igniter CMS includes a built-in CRON controller to automate background tasks. These tasks are managed in app/Controllers/CronController.php.
Define a strong secret key in your .env file to prevent unauthorized access:
CRON_SECRET_KEY = "your-strong-secret-key-here-12345"
Choose one of the following methods to trigger your CRON tasks every minute:
If you are on shared hosting or don't want to touch server logs, use a free service like cron-job.org or EasyCron.
https://yourdomain.com/cron/run?key=your-secret-keyAdd this line to your server's crontab file to trigger the script internally:
* * * * * curl -s "https://yourdomain.com/cron/run?key=your-secret-key" > /dev/null 2>&1
When the CRON runs successfully, it returns a JSON response and logs the activity:
{
"status": "success",
"message": "Cron job(s) executed successfully",
"timestamp": "2026-02-17 13:22:23"
}
Igniter CMS integrates with Google Gemini to provide advanced AI features, including blog generation assistance and automated data analysis for site logs and visits.
To begin, you must add your Gemini API credentials to your .env file. You can obtain a key from the Google AI Studio.
GEMINI_REQUEST_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent"
GEMINI_REQUEST_KEY = "your-gemini-key-here"
Once your credentials are set, you must enable the features within the Admin Dashboard:
/account/admin/configurations).Yes to activate the AI "Generate" buttons in the blog and content editors.Yes if you want the system to use AI to analyze site traffic, error logs, and user behavior. Set to No to disable data processing while keeping text generation active.gemini-3-flash-preview model by default for its high speed and low latency, making it ideal for real-time content generation.
Igniter CMS features a powerful theme system that separates presentation logic from core functionality. Themes are self-contained directories that include view templates, static assets, and configuration. This guide will help you create professional, feature-rich themes for your CMS installation.
Every theme must follow this exact structure to be recognized by the CMS:
theme-name/
├── assets/
│ ├── css/
│ │ └── site.css # Your custom styles (optional)
│ ├── images/
│ │ └── preview.png # Theme preview image (740x460px recommended)
│ └── js/
│ └── site.js # Your custom JavaScript (optional)
├── views/
│ ├── blogs/
│ │ ├── index.php # Blog listing page
│ │ └── view-blog.php # Single blog post page
│ ├── home/
│ │ └── index.php # Homepage template
│ ├── includes/
│ │ └── _functions.php # Theme-specific helper functions
│ ├── layout/
│ │ └── _layout.php # Master layout template
│ ├── pages/
│ │ └── view-page.php # Static page template
│ └── search/
│ ├── filter.php # Filtered search results (category/tag/author)
│ └── index.php # General search results
└── theme.json # Theme configuration file
Each template file serves a specific purpose and receives specific data from the CMS:
| Template File | Purpose | Available Variables |
|---|---|---|
layout/_layout.php |
Master wrapper containing HTML structure, navigation, and footer | $theme, $siteName, $themeData[], $topNavLists, $footerNavLists, $servicesNavLists |
home/index.php |
Front page content display | $theme, $currentPage, $popUpWhereClause, $enableHomeSeo |
blogs/index.php |
Blog listing with pagination | $blogs, $pager, $total_blogs, $theme, $currentPage |
blogs/view-blog.php |
Single blog post with sidebar | $blog_data, $categories, $blogs, $theme |
pages/view-page.php |
Static CMS page display | $page_data, $theme, $currentPage |
search/index.php |
General search results page | $searchQuery, $blogsSearchResults, $pagesSearchResults, $theme |
search/filter.php |
Filtered results (category/tag/author) | $searchQuery, $blogsSearchResults, $pagesSearchResults, $type, $theme |
The theme.json file defines your theme's metadata and visual settings. Here's a complete example with all available options:
{
"name": "My Awesome Theme",
"path": "my-awesome-theme",
"description": "A modern, responsive theme for business websites",
"version": "1.0.0",
"author": "Your Name",
"email": "your@email.com",
"author_url": "https://yourwebsite.com",
"theme_url": "https://yourwebsite.com/theme",
"image": "preview.png",
"category": "Business",
"sub_category": "Corporate",
"default_color": "#6c757d",
"heading_color": "#212529",
"accent_color": "#0d6efd",
"surface_color": "#f8f9fa",
"contrast_color": "#0d6efd",
"background_color": "#ffffff",
"use_static_theme_nav": 0,
"override_default_style": 1,
"plugins_required": "seo-master,easy-hide-login",
"license": "MIT",
"requires": {
"php": ">=8.0",
"igniter-cms": ">=2.0"
}
}
path must match the theme folder name exactlyuse_static_theme_nav: Set to 1 to use hardcoded navigation instead of database-driven menusoverride_default_style: Set to 1 to enable custom CSS variables from theme.jsonplugins_required: Comma-separated list of plugin slugs required for theme functionality. If non required, leave empty.Your theme has access to these essential helper functions (defined in includes/_functions.php or available globally):
| Function | Description | Example |
|---|---|---|
getCurrentTheme() |
Returns the active theme folder name | $theme = getCurrentTheme(); |
getThemeData($theme, $key) |
Retrieves a specific value from theme.json | $color = getThemeData($theme, "accent_color"); |
getConfigData($key) |
Retrieves system configuration values | $siteName = getConfigData("SiteName"); |
getLinkUrl($link) |
Processes internal/external URLs safely | $url = getLinkUrl($navigation['link']); |
getImageUrl($path) |
Returns full URL for image assets | <img src="<?= getImageUrl($logoPath) ?>"> |
renderAdminBar() |
Renders admin toolbar for logged-in users | <?= renderAdminBar() ?> |
themef_renderNavigation($nav, $model) |
Recursively renders navigation with dropdowns (defined in _functions.php) |
<?= themef_renderNavigation($navigation, $navigationsModel) ?> |
The layout file is the foundation of your theme. Here's a minimal working example:
<?php
$theme = getCurrentTheme();
$siteName = getConfigData("SiteName");
// Load theme data from theme.json
$themeData = [
'accentColor' => getThemeData($theme, "accent_color"),
'backgroundColor' => getThemeData($theme, "background_color"),
];
// Load navigation
$navigationsModel = new \App\Models\NavigationsModel();
$topNavLists = $navigationsModel->where('group', 'top_nav')->findAll();
?>
<?= $this->include('front-end/themes/'.$theme.'/includes/_functions.php'); ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= $siteName ?></title>
<!-- Bootstrap 5 (required) -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Theme Custom CSS -->
<link href="<?= base_url('public/front-end/themes/'.$theme.'/assets/css/site.css') ?>" rel="stylesheet">
</head>
<body>
<?= renderAdminBar() ?>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="<?= base_url() ?>">
<?= $siteName ?>
</a>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<?php foreach ($topNavLists as $navigation): ?>
<?= themef_renderNavigation($navigation, $navigationsModel) ?>
<?php endforeach; ?>
</ul>
</div>
</div>
</nav>
<!-- Main Content Area -->
<main>
<?= $this->renderSection('content') ?>
</main>
<!-- Footer -->
<footer class="bg-dark text-white text-center py-3">
<p>© <?= date('Y') ?> <?= $siteName ?>. All rights reserved.</p>
</footer>
<!-- Bootstrap JS Bundle -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="<?= base_url('public/front-end/themes/'.$theme.'/assets/js/site.js') ?>"></script>
</body>
</html>
Igniter CMS provides built-in helper functions that render consistent, theme-agnostic output. These are perfect for rapid development:
renderBlogContent($blog_data) - Displays full blog post with formatted contentrenderBlogSidebar($categories, $recentBlogs, $currentBlog) - Renders categories and recent posts sidebarrenderBlogsGrid($blogs) - Creates responsive blog listing gridrenderSearchResults($query, $blogs, $pages) - Unified search results displayrenderFilterSearchResults($query, $blogs, $pages, $type) - Filtered results with type indicationThe themef_renderNavigation() function handles multi-level menus automatically. Define it in includes/_functions.php:
<?php
/**
* Renders navigation with dropdown support for child items
*/
function themef_renderNavigation(array $navigation, object $navigationsModel): string
{
$navId = $navigation['navigation_id'];
$title = $navigation['title'];
$link = getLinkUrl($navigation['link']);
$target = $navigation['new_tab'] === "1" ? "_blank" : "_self";
// Get child items
$children = $navigationsModel->where('parent', $navId)
->orderBy('order', 'ASC')
->findAll();
if (empty($children)) {
return '<li class="nav-item">
<a class="nav-link" href="'.$link.'" target="'.$target.'">
'.$title.'
</a>
</li>';
}
// Render dropdown menu
$output = '<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">
'.$title.'
</a>
<ul class="dropdown-menu">';
foreach ($children as $child) {
$childLink = getLinkUrl($child['link']);
$childTarget = $child['new_tab'] === "1" ? "_blank" : "_self";
$output .= '<li><a class="dropdown-item" href="'.$childLink.'" target="'.$childTarget.'">
'.$child['title'].'
</a></li>';
}
$output .= '</ul></li>';
return $output;
}
?>
When override_default_style is enabled in theme.json, your CSS variables are automatically injected:
/* Use these variables in your site.css */
:root {
--default-color: #6c757d; /* Body text and default elements */
--heading-color: #212529; /* Headings (h1-h6) */
--accent-color: #0d6efd; /* Buttons, links, primary actions */
--surface-color: #f8f9fa; /* Cards, modals, secondary backgrounds */
--contrast-color: #0d6efd; /* Highlights and focus states */
--background-color: #ffffff; /* Page background */
}
/* Example usage */
.btn-primary {
background-color: var(--accent-color);
border-color: var(--accent-color);
}
.card {
background-color: var(--surface-color);
color: var(--default-color);
}
h1, h2, h3 {
color: var(--heading-color);
}
Place your JavaScript in assets/js/site.js. It will be automatically loaded in the footer. For page-specific scripts, use conditional loading:
// assets/js/site.js
document.addEventListener('DOMContentLoaded', function() {
// Initialize tooltips
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function(tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
// Mobile menu handling
const navbarToggler = document.querySelector('.navbar-toggler');
if (navbarToggler) {
navbarToggler.addEventListener('click', function() {
document.querySelector('.navbar-collapse').classList.toggle('show');
});
}
});
<?php if (ENVIRONMENT !== 'production'): ?> to hide them in production
<!-- In blogs/index.php, replace <?= renderBlogsGrid($blogs) ?> with: -->
<div class="row">
<?php foreach ($blogs as $blog): ?>
<div class="col-md-4 mb-4">
<div class="card h-100">
<?php if (!empty($blog['featured_image'])): ?>
<img src="<?= getImageUrl($blog['featured_image']) ?>"
class="card-img-top" alt="<?= esc($blog['title']) ?>">
<?php endif; ?>
<div class="card-body">
<h5 class="card-title"><?= esc($blog['title']) ?></h5>
<p class="card-text"><?= character_limiter(strip_tags($blog['content']), 150) ?></p>
<a href="<?= base_url('blogs/' . $blog['slug']) ?>" class="btn btn-primary">
Read More
</a>
</div>
<div class="card-footer text-muted">
Posted on <?= date('F j, Y', strtotime($blog['created_at'])) ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<!-- In search/index.php, replace helper function with: -->
<h2>Search Results for "<?= esc($searchQuery) ?>"</h2>
<?php if (empty($blogsSearchResults) && empty($pagesSearchResults)): ?>
<div class="alert alert-info">
No results found. Please try different keywords.
</div>
<?php else: ?>
<?php foreach ($blogsSearchResults as $blog): ?>
<div class="search-result mb-4">
<h3><a href="<?= base_url('blogs/' . $blog['slug']) ?>">
<?= esc($blog['title']) ?>
</a></h3>
<p><?= character_limiter(strip_tags($blog['content']), 200) ?></p>
<small>Blog Post</small>
</div>
<?php endforeach; ?>
<?php foreach ($pagesSearchResults as $page): ?>
<div class="search-result mb-4">
<h3><a href="<?= base_url('page/' . $page['slug']) ?>">
<?= esc($page['title']) ?>
</a></h3>
<p><?= character_limiter(strip_tags($page['content']), 200) ?></p>
<small>Page</small>
</div>
<?php endforeach; ?>
<?php endif; ?>
theme.json with valid JSON syntaxlayout/_layout.php master templatehome/index.php homepage templateblogs/index.php and view-blog.phppages/view-page.php static page templateesc() to escape output and prevent XSS attacks: <?= esc($variable) ?>Plugins allow you to extend the core functionality of Igniter CMS without modifying system files. Every plugin must be self-contained within its own folder.
A standard plugin requires the following 6 files to function correctly:
plugin.json contains a unique plugin_id to avoid conflicts with existing system plugins.
Copyright (c) 2025 Abdoulie Kassama
This project is licensed under the MIT License - see the LICENSE file for details.
Blog Comments
Use to manage comment form submissions