Control Layer
Control Layer Reference
The Control Layer is the collection of individual controller files stored in inc/lib/Control/. These files are not called directly — they are loaded by the Control class dispatcher based on the current URL or admin page selection.
Directory Structure
inc/lib/Control/
├── Frontend/ — Public-facing page controllers (class-based, extend BaseControl)
├── Backend/ — Admin dashboard controllers (procedural)
├── Ajax/ — JSON API / AJAX endpoint controllers
├── Api/ — RESTful Resource controllers (class-based)
├── Error/ — Error page handlers
└── Install/ — Installation wizard controller
Frontend Controllers
All frontend controllers are class-based and extend BaseControl, which provides the Latte templating engine and common data. Each file instantiates its class and calls run($param) at the bottom.
How they are called:
// GxMain calls:
Control::frontend();
// Which calls:
Control::incFront('post', $param);
// Which includes:
inc/lib/Control/Frontend/post.control.php
| File | Class | URL Pattern | Description |
|---|---|---|---|
default.control.php |
DefaultControl |
/ or /paging/N/ |
Homepage with paginated post listing |
post.control.php |
PostControl |
/slug.html |
Single post or page detail view |
page.control.php |
PageControl |
/slug.html (type=page) |
Static page with optional module support |
cat.control.php |
CatControl |
/category/ID/slug/ |
Category archive listing |
tag.control.php |
TagControl |
/tag/slug/ |
Tag archive listing |
author.control.php |
AuthorControl |
/author/username/ |
Author post archive |
archive.control.php |
ArchiveControl |
/YYYY/MM/ |
Date-based monthly archive |
search.control.php |
SearchControl |
/search/ |
Full-text search results |
login.control.php |
LoginControl |
/login/ |
User login form and processing |
register.control.php |
(procedural) | /register/ |
User self-registration with email verification and account activation |
forgotpass.control.php |
(procedural) | /forgotpass/ |
Password reset — generates new password and emails it to user |
logout.control.php |
(procedural) | /logout/ |
Destroy session and redirect to home |
mod.control.php |
ModControl |
/mod/MODULE_NAME.html |
Custom module frontend page loader (extends BaseControl) |
rss.control.php |
(procedural) | /rss/ |
RSS feed XML output |
sitemap.control.php |
(procedural) | /sitemap.xml |
XML sitemap output |
thumb.control.php |
(procedural) | /thumb/type/SIZE/ALIGN/file |
Dynamic image thumbnail via Image::thumbFly |
Standard Frontend Controller Pattern
class PostControl extends BaseControl
{
public function run($param)
{
// 1. Extract URL parameters
$data = Router::scrap($param);
// 2. Fetch & prepare content
$posts = Posts::fetch(['id' => $post_id]);
$data['posts'] = Posts::prepare($posts);
// 3. Trigger hooks
$data['content'] = Hooks::run('post_content_before_action', $data);
$data['content'] .= Posts::content($post->content);
// 4. Render through BaseControl (header + view + footer via Latte)
$this->render('single', $data);
// 5. Track page view
Stats::addViews($post_id);
}
}
// Instantiate and run at end of file:
$control = new PostControl();
$control->run($param);
Backend Controllers
Backend controllers are procedural files with no class definition. They are loaded by Control::incBack(). All operations are protected by User::access(level).
How they are called:
// Admin URL: /admin/index.php?page=posts
Control::backend();
// Which calls:
Control::incBack('posts');
// Which includes:
inc/lib/Control/Backend/posts.control.php
| File | ?page= Value |
Min Access Level | Description |
|---|---|---|---|
default.control.php |
(dashboard) | 1 | Main admin dashboard with stats |
posts.control.php |
posts |
3 | Blog post CRUD (add, edit, delete, bulk actions) |
pages.control.php |
pages |
3 | Static page CRUD |
categories.control.php |
categories |
1 | Post category add/edit/delete |
tags.control.php |
tags |
1 | Tag management |
comments.control.php |
comments |
1 | Comment moderation |
menus.control.php |
menus |
1 | Navigation menu builder |
modules.control.php |
modules |
1 | Module listing and install/uninstall |
mods.control.php |
mod |
1 | Individual module backend page loader |
media.control.php |
media |
1 | Media file browser (elFinder) |
themes.control.php |
themes |
1 | Theme listing and activation |
users.control.php |
users |
1 | User management (CRUD, roles) |
widgets.control.php |
widgets |
1 | Widget placement management |
settings.control.php |
settings |
1 | General site settings |
settings-cache.control.php |
settings-cache |
1 | File/Redis cache settings |
settings-comments.control.php |
settings-comments |
1 | Comment system settings |
settings-media.control.php |
settings-media |
1 | Image, watermark, and media settings |
settings-multilang.control.php |
settings-multilang |
1 | Multi-language configuration |
settings-permalink.control.php |
settings-permalink |
1 | Permalink / URL structure settings |
permissions.control.php |
permissions |
0 | User role and access permission settings |
User Access Levels
| Level | Role | Notes |
|---|---|---|
| 0 | Administrator | Full access — including system settings, themes, modules, and permissions |
| 1 | Supervisor | Site-wide management (all backend areas except system-level settings) |
| 2 | Editor | Content management and moderation |
| 3 | Author | Manage their own content only |
| 4 | Contributor | Post creation (review required before publish) |
| 6 | General Member | Default site member — frontend access only |
Standard Backend Controller Pattern
if (User::access(1)) {
// Sanitize inputs
$name = Typo::cleanX($_POST['name']);
$token = Typo::cleanX($_POST['token']);
// Validate CSRF token
if (!Token::validate($token)) {
$alertDanger[] = _("Token not exist or expired.");
}
// Validate required fields
if (empty($name)) {
$alertDanger[] = _("Name cannot be empty.");
}
if (!isset($alertDanger)) {
// Database operation
Db::insert("INSERT INTO `table` VALUES (...)");
$data['alertSuccess'][] = _("Saved successfully.");
Token::remove($token);
} else {
$data['alertDanger'] = $alertDanger;
}
// Render admin template
Theme::admin('header', $data);
System::inc('view_name', $data);
Theme::admin('footer');
} else {
Theme::admin('header');
Control::error('noaccess');
Theme::admin('footer');
}
Ajax Controllers
Ajax controllers are procedural files that return JSON responses. They are loaded by Control::ajax() when the URL matches /ajax/{endpoint}/{token}.
How they are called:
// URL: /ajax/api/TOKEN
Control::ajax('api', $param);
// Which includes:
inc/lib/Control/Ajax/api-ajax.control.php
| File | Endpoint | Token Required | Description |
|---|---|---|---|
api-ajax.control.php |
api |
Optional (per action) | REST-like CMS data API (posts, categories, tags) |
elfinder-ajax.control.php |
elfinder |
Yes | elFinder file manager backend |
saveimage-ajax.control.php |
saveimage |
Yes | Saves images pasted into the WYSIWYG editor |
tags-ajax.control.php |
tags |
Yes | Tag autocomplete suggestions |
version-ajax.control.php |
version |
Yes | Checks for latest GeniXCMS version |
Public API Endpoints (api-ajax)
Some actions are publicly accessible without a token:
Action (?action=) |
Token | Parameters | Returns |
|---|---|---|---|
recent_posts |
Not required | num, type, cat |
Array of post objects with image, excerpt |
categories |
Not required | type |
Array of category objects |
tags |
Required | term |
Array of matching tag objects |
Example Request:
GET /ajax/api/TOKEN?action=recent_posts&num=5
Example Response:
{
"status": "success",
"data": [
{
"id": 42,
"title": "Hello World",
"url": "https://example.com/hello-world.html",
"image": "https://example.com/thumb/type/square/size/300/...",
"excerpt": "Short preview..."
}
]
}
API Controllers (REST Resource)
API controllers are class-based files located in inc/lib/Control/Api/. These controllers follow a standard RESTful architecture, mapping HTTP verbs to specific class methods. They are dispatched by the Api class.
How they are called:
// URL: /api/v1/posts/123 (GET)
Control::api('posts', '123');
// Which triggers Api::dispatch('posts', '123')
// and calls PostsApi->index('123')
| File | Class | Endpoint | Methods Support |
|---|---|---|---|
PostsApi.class.php |
PostsApi |
/api/v1/posts |
GET, POST, PUT, DELETE |
PagesApi.class.php |
PagesApi |
/api/v1/pages |
GET, POST, PUT, DELETE |
Standard API Controller Pattern
All methods return a JSON response via Api::success() or Api::error().
class PostsApi
{
/**
* Maps to GET /api/v1/posts/ or GET /api/v1/posts/{id}
*/
public function index($id = null)
{
if ($id) {
$post = Posts::find($id);
return $post ? Api::success($post) : Api::error(404, 'Post not found');
}
$posts = Posts::all();
return Api::success($posts);
}
/**
* Maps to POST /api/v1/posts/
*/
public function submit()
{
$data = json_decode(file_get_contents('php://input'), true);
$res = Posts::create($data);
return $res ? Api::success(['id' => Db::$last_id]) : Api::error(500, 'Failed creation');
}
/**
* Maps to PUT or PATCH /api/v1/posts/{id}
*/
public function update($id)
{
$post = Posts::find($id);
if (!$post) return Api::error(404, 'Post not found');
$data = json_decode(file_get_contents('php://input'), true);
return $post->save($data) ? Api::success(null, 'Updated') : Api::error(500, 'Failed');
}
/**
* Maps to DELETE /api/v1/posts/{id}
*/
public function delete($id)
{
$post = Posts::find($id);
return $post && $post->destroy() ? Api::success(null, 'Deleted') : Api::error(500, 'Failed');
}
}
Error Controllers
Error controllers are simple include files at inc/lib/Control/Error/ triggered by Control::error($code).
| File | Code | Description |
|---|---|---|
404.control.php |
404 |
Page not found |
noaccess.control.php |
noaccess |
Insufficient user permission |
unknown.control.php |
(default) | Generic unknown error |