Admin Integration
Rendering a page inside the admin panel
Plugins never implement their own admin layout. admin/index.php?action=plugin_page renders your content inside the standard chrome — same sidebar, top bar, and footer as every built-in admin screen. You define one function:
function my_plugin_render_admin_page(string $view): array
{
// $view comes from ?view=... in the URL — your plugin defines what
// views exist ('overview', 'settings') and what to render for each.
return [
'title' => 'My Plugin',
'html' => '<p>Hello, view: ' . htmlspecialchars($view) . '</p>',
'extra_head' => '', // optional <link>/<style>/<script> for <head>
];
}
Hyphens in your slug become underscores in the function name — a plugin with slug my-plugin defines my_plugin_render_admin_page. The core router handles session/auth, loading your entry file if it isn't already loaded, calling your render function, and wrapping the result in includes/layout.php.
Building admin URLs
Your plugin's admin page lives at:
{cms_base_url}/admin/index.php?action=plugin_page&slug=my-plugin&view={view}
Build this from your own filesystem position rather than hardcoding a path — the CMS may be installed at the domain root or a sub-directory, and the admin folder name is configurable at install time (admin_dir in config.json). Never hardcode admin/:
function my_plugin_admin_url(string $view, array $extraParams = []): string
{
$params = array_merge(['action' => 'plugin_page', 'slug' => 'my-plugin', 'view' => $view], $extraParams);
$configFile = dirname(__DIR__, 2) . '/config.json'; // adjust depth to your file's location
$adminDir = 'admin';
if (file_exists($configFile)) {
$decoded = json_decode(file_get_contents($configFile), true);
if (is_array($decoded) && !empty($decoded['admin_dir'])) {
$adminDir = $decoded['admin_dir'];
}
}
return site_base_url() . $adminDir . '/index.php?' . http_build_query($params);
}
Always build absolute URLs (full https://host/path/...) — your own view templates, and any return_url field in a form that posts to your plugin's own endpoint, need to resolve correctly regardless of which physical file the browser is currently on. A relative path breaks the moment your action handler lives in a different folder than /admin/.
Matching the admin's visual style
If your admin page uses any custom CSS beyond what the standard layout already provides, use the admin panel's own CSS custom properties (--surface, --border, --primary, --danger, --radius-sm, etc. — see Admin UI Styling) rather than hardcoded colors, so your plugin's page follows the same light/dark theming as the rest of the panel automatically.
